<?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=FalkB</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=FalkB"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/FalkB"/>
	<updated>2026-04-12T12:02:35Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25480</id>
		<title>DDS</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25480"/>
		<updated>2008-01-11T23:44:04Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Lesestoff */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Direkte Digitale Synthese===&lt;br /&gt;
Erzeugung einer Wechselspannung / eines Wechselstromes mittels Frequenzgenerator, FixedPoint-Addierer, LookUpTable und Digital-Analog-Wandler [[DAC]].&lt;br /&gt;
&lt;br /&gt;
==Funktionsweise==&lt;br /&gt;
Erzeugung der Basisfrequenz durch Quarzoszillator (evtl. mit Phase-Locked-Loop PLL Synthesizer). Ausgangsfrequenz ist immer geringer als die Basisfrequenz.&lt;br /&gt;
&lt;br /&gt;
Sehr gute Beschreibung der Theorie und der Nachteile (PhasenRauschen, Oberwellen):&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.analog.com/UploadedFiles/Tutorials/450968421DDS_Tutorial_rev12-2-99.pdf&lt;br /&gt;
&lt;br /&gt;
Die LookUpTable kann fest (meist Sinus) oder programmierbar (RAM, Arbitrary Waveform) oder ein Algorithmus sein.&lt;br /&gt;
&lt;br /&gt;
Verwendung im NF / Audio- und HF / Funk-Bereich (bis 1 GHz).&lt;br /&gt;
&lt;br /&gt;
Hersteller: Analog Devices http://www.analog.com&lt;br /&gt;
&lt;br /&gt;
Bekannte ICs: Fairchild ML2037, AD9833 (25MHz), AD9835, AD9851, AD9854 (300MHz), AD9951 (400MHz)&lt;br /&gt;
&lt;br /&gt;
Die modernener Chips beinhalten weitere Funktionen wie digitale Amplitudenmodulation (AM), Phasenmodulation (PM), Frequency Shift Keying (FSK).&lt;br /&gt;
&lt;br /&gt;
==Projekte:==&lt;br /&gt;
*[[Digitaler Funktionsgenerator]]&lt;br /&gt;
*HF-Meßplatz: http://www.mikrocontroller.net/forum/read-1-350237.html&lt;br /&gt;
* Einfache Schaltung: 6 Drehschalter - PIC16F84 - AD9833: http://www.mikrocontroller.net/forum/read-1-403708.html&lt;br /&gt;
* Ganz ähnlich, mit ML2037: http://www.seattlerobotics.org/encoder/200205/ddsfgen.htm&lt;br /&gt;
* DDS-Signalgenerator bis 500 MHz (1200MHz Takt): http://www.dg4rbf.de/ -&amp;gt; SYN500, SYN500/2&lt;br /&gt;
&lt;br /&gt;
=Lesestoff=&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=dds* Suche nach dds*]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=synthe* Suche nach synthe*]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#new Lange, ausführliche Diskussion zur Berechung des Frequency Tuning Words]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#710690 Praktische Umsetzung]&lt;br /&gt;
&lt;br /&gt;
Implementierung im NF-Bereich auch durch µC möglich, z.B. für DialTone (DTFM):&lt;br /&gt;
* [http://www.myplace.nu/avr/minidds/index.htm Jesper&#039;s AVR pages, DDS using an AVR]&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc1982.pdf Atmel Application Note]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=sinus* Suche nach *sinus* im Forum]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=tonerzeug* Suche nach tonerzeug* im Forum]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Midi_Rekorder_mit_MMC/SD-Karte&amp;diff=25479</id>
		<title>Midi Rekorder mit MMC/SD-Karte</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Midi_Rekorder_mit_MMC/SD-Karte&amp;diff=25479"/>
		<updated>2008-01-11T23:24:51Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Einrichtung ATmega168 mit MMC/SD-Bootloader */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;von [[Benutzer:Dersimon]]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Foto.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* Aufnahme, Wiedergabe, Vor- und Rückspulen&lt;br /&gt;
* LCD, Zeit- und Sektoranzeige&lt;br /&gt;
* Speichert bis zu 65536 &amp;quot;Files&amp;quot; auf MMC/SD-Karte (optimale Kartengröße 16 MB)&lt;br /&gt;
* Maximale unterstützte Kapazität der Karte 8GB (!)&lt;br /&gt;
* Beliebige MMC/SD-Karten verwendbar, allerdings muss sie mit Windows formatiert werden und dann muss ein leeres File mit etwa der Kapazität der Karte (unter Eigenschaften im Windows-Explorer wird der freie Speicher in Bytes angegeben) drauf kopiert werden. (Das kann irgendeine Datei sein, die halt gerade noch draufpasst.) Erst dann sollte man sie in Mr.MIDI stecken :-) Sollte die Karte größer als 16 MB sein, kann man die Dateigröße so berechnen: 512 * 256 * Anzahl &amp;quot;Files&amp;quot;, dann allerdings nicht in Mr.MIDI zu große Filenummern verwenden! Sonst geht es auf der Karte so durcheinander, dass Windows sie neu formatieren muss...&lt;br /&gt;
* PC-Tool zum Umwandeln von/in PC-MIDI-Dateien (nur FORMAT 0)&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
* [[AVR|ATmega8]] mit internem RC-Takt 1 MHz&lt;br /&gt;
* [[MIDI]] Interface mit Optokoppler 6N138&lt;br /&gt;
* [[LCD]] Display 24*2 (Standardcontroller)&lt;br /&gt;
* 7 Tasten und 1 Anschalter&lt;br /&gt;
* Spannungsregler 5V und 3,3V&lt;br /&gt;
* MMC/SD-Slot&lt;br /&gt;
&lt;br /&gt;
== Kompilieren ==&lt;br /&gt;
* In AVR Studio 4 Projekt laden, alle *.asm Dateien hinzufügen&lt;br /&gt;
* Build (F7) drücken&lt;br /&gt;
* Hex-Code in Controller laden&lt;br /&gt;
* Fertig!&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
* Medion-SD-Karten gehen nicht.&lt;br /&gt;
* PC-Tool hat eine Macke beim Parsen von Mididateien (Wurde behoben, neue Version hier im Artikel zum Download)&lt;br /&gt;
&lt;br /&gt;
== Foto ==&lt;br /&gt;
Auf dem Foto sieht man mein Endgerät im hübschen Acrylgehäuse.&lt;br /&gt;
Rechts sind die 2 MIDI-Kabel zum Keyboard.&lt;br /&gt;
&lt;br /&gt;
== Schaltplan ==&lt;br /&gt;
[[Bild:Eagle-schematic.gif|thumb|400px|left|Schaltplan des MIDI-Rekorders]]&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear:both;&amp;quot;/&amp;gt;&lt;br /&gt;
MIDI-Out falsch beschaltet: Pin 4 und 5 tauschen!&lt;br /&gt;
&lt;br /&gt;
== Download des Projekts (inkl. Code, Schaltplan) ==&lt;br /&gt;
&lt;br /&gt;
[[Media:Zip.zip|Zip.zip]]&lt;br /&gt;
&lt;br /&gt;
Neuer Konverter (Bugfix):&lt;br /&gt;
[[Media:MrMIDIConverter.zip|MrMIDIConverter.zip]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Mr.MIDI 2 ist da ! ==&lt;br /&gt;
Endlich ist es soweit: Der neue Mr.Midi arbeitet mit dem echten Mididatei-Format (Format 0).&lt;br /&gt;
Aufnahme und Wiedergabe sind so einfach wie bei einem MP3-Player.&lt;br /&gt;
Und dennoch bleibt die Hardware gleich! Das heißt, alle Nachbauer brauchen nur den neuen Code flashen und haben den neuen Mr.Midi 2!&lt;br /&gt;
Für alle, die es noch besser wollen: Echte FAT16-Unterstützung (kann mit defragmentierten Dateien umgehen und selber in ein fragmentiertes Dateisystem schreiben! Dabei wird die Karte so gut es geht geschont!) Für solchen Luxus muss man dann doch den ATmega168 einsetzen :-)&lt;br /&gt;
&lt;br /&gt;
English translation on my homepage http://www.lehmayr.de available!&lt;br /&gt;
&lt;br /&gt;
[[Bild:Foto_MrMidi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Features ===&lt;br /&gt;
* Echte Mididateien auf MMC/SD-Karte (Format 0) können abgespielt werden&lt;br /&gt;
* Bis zu 255 Dateien werden unterstützt&lt;br /&gt;
* Aufzeichnung erfolgt ebenfalls im Midi-Format 0 (jetzt auch mit FAT-Eintrag)&lt;br /&gt;
* Löschen von Dateien ist möglich (jetzt auch mit FAT-Aktualisierung)&lt;br /&gt;
* 256 Byte Sende/Empfangspuffer und 8MHz-Takt für hohe Performance und präzises MIDI-Timing&lt;br /&gt;
* Lyrics werden im Display angezeigt (Coole Sache!)&lt;br /&gt;
* Tempo in +/-15 Schritten einstellbar (vorbereitet auf stufenlose Analogknopf-Einstellung, Hardwareänderung erforderlich)&lt;br /&gt;
* Bootloader lädt neue Firmware von MMC/SD-Karte (frisch formatiert und nur Datei MM.BIN drauf)&lt;br /&gt;
* Transponierfunktion&lt;br /&gt;
* Muting ausgewählter Midi-Channels&lt;br /&gt;
* Menü für Einstellung Schlagzeugkanal, Lyrikanzeige und Such-Startsektor&lt;br /&gt;
* Nichtflüchtige Parameter werden im EEPROM gespeichert&lt;br /&gt;
* Verschiedene Repeatmodi&lt;br /&gt;
* LED-Beleuchtung im Menü schaltbar (auf PORTB 6+7)&lt;br /&gt;
&lt;br /&gt;
=== Download Mr.MIDI 2 ===&lt;br /&gt;
* ATmega8 (ohne Bootloader): [[Media:MrMidi2.zip|MrMidi2.zip]]&lt;br /&gt;
* ATmega168 (mit Bootloader): [[Media:MrMidi2m168.zip|MrMidi2m168.zip]]&lt;br /&gt;
* ATmega168 (mit Bootloader) für IR-Fernbedienung: [[Media:MrMidi2m168IR.zip|MrMidi2m168IR.zip]]&lt;br /&gt;
* ATmega168 (mit Bootloader) für IR-Fernbedienung und mit FAT16-Unterstützung, sowie dem neuen MMC/SD-Karten-Bootloader: [[Media:MrMidi2m168IRFAT16.zip|MrMidi2m168IRFAT16.zip]]&lt;br /&gt;
* Neue Version mit 32-Bit-Sektoradressierung: [[Media:MrMidi2m168IRFAT16_32.zip|MrMidi2m168IRFAT16_32.zip]]&lt;br /&gt;
* Neue &amp;quot;Ultimate&amp;quot;-Version (massiv optimiert, auf Laufzeit und Größe, neuer 512-Word-Bootloader): [[Media:MrMidi2Ultimate.zip|MrMidi2Ultimate.zip]]&lt;br /&gt;
* HEX2BIN-Tool (benötigt um die binären Dateien für den MMC/SD-Karten-Bootloader zu erzeugen): http://www.keil.com/download/docs/7.asp?bhjs=0&lt;br /&gt;
&lt;br /&gt;
Achtung: Leider hat sich der WinAVR Compiler irgendwann so geändert, so dass die gleichen Sources neu kompiliert nicht mehr den Bootloader starten können!!!&lt;br /&gt;
Daher BL defekt bei allen alten 32-Bit-Versionen (haben auf &amp;quot;_2.zip&amp;quot; und &amp;quot;_3.zip&amp;quot; geendet)!&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ATmega168 mit MMC/SD-Bootloader ====&lt;br /&gt;
* ATmega168 Fuses auf &amp;quot;1024 Word Bootsize&amp;quot; und KEIN &amp;quot;Bootvector enabled&amp;quot; setzen (optional BOD 4,3V auf enabled)&lt;br /&gt;
* Internen RC-Oszillator 8MHz und KEINEN Clockdivider/8 verwenden - alternativ und empfohlen: 8MHz Quarz verwenden! Neuere ATmegas haben offenbar einen Sch***-internen Oszillator. (Kommentar: Nöö, einen UART bertreibt man IMMER mit einem Quarz. Aus gutem Grund! Siehe [[AVR-Tutorial: UART]])&lt;br /&gt;
* Leere MCU: Firmwaredatei mrmidi2_bl.hex flashen (wie du das machst, ist egal)&lt;br /&gt;
* Bootloader ist bereits installiert:&lt;br /&gt;
** Karte formatieren (FAT16) und die Datei MM.BIN draufkopieren&lt;br /&gt;
** Karte einlegen, Mr.MIDI 2 anschalten&lt;br /&gt;
** Warten bis fertig&lt;br /&gt;
** Karte entnehmen, Mr. MIDI 2 ausschalten, original Karte einstecken und wieder anschalten&lt;br /&gt;
&lt;br /&gt;
==== Änderungen seit Initialrevision ====&lt;br /&gt;
* Parameter im EEPROM&lt;br /&gt;
* Menü&lt;br /&gt;
* Transponierfunktion&lt;br /&gt;
* Schnelleres MMC Interface (2MHz)&lt;br /&gt;
* MMC_init angepasst, um noch mehr Karten zu unterstützen&lt;br /&gt;
* Lyric-Anzeige&lt;br /&gt;
* Bootloader über MIDI&lt;br /&gt;
* Channel-Muting&lt;br /&gt;
* Card-Scan ist jetzt intelligenter (FAT16/FAT32-Karten werden unterstützt)&lt;br /&gt;
* verschiedene Bugfixes&lt;br /&gt;
* Fernbedienungsfunktion RC5&lt;br /&gt;
* Display 16x2-Zeichen wird auch unterstützt (alternative Firmware, per Defineschalter im Code wählbar, im Download natürlich auch als extra kompiliertes HEX-File enthalten)&lt;br /&gt;
* Verschiedene Wiederholungsmodi für die Songs&lt;br /&gt;
* Sendepuffer (präzises MIDI-Timing einhalten, auch wenn FAT-Lesezugriffe auf die Karte erfolgen)&lt;br /&gt;
* LED-Hintergrundbeleuchtung im Menü schaltbar (PORTB 6+7)&lt;br /&gt;
* FAT16-Unterstützung (vollwertig)&lt;br /&gt;
** Lange Dateinamen&lt;br /&gt;
** Anzeige Dateigröße&lt;br /&gt;
** Dateien aufzeichnen und am PC kopieren&lt;br /&gt;
** Dateien löschen&lt;br /&gt;
* 32-Bit-Sektoradressierung&lt;br /&gt;
* mehr Bugfixes&lt;br /&gt;
* MMC/SD-Bootloader integriert&lt;br /&gt;
* Gerät jetzt seit mehreren Monaten bei einigen Leuten im Einsatz und noch keine Probleme (Kompatibilität von MMC/SD-Karten gemeldet.&lt;br /&gt;
* Noch mehr Bugfixes (Bootloaderfehler-mit-WinAVR20070525-Bug, 32-Bit-Sektornummern-Bug, Abbruch von Aufnahme-Bug, Random-Play-Bug, 16-Dateien-Bug, etc.)&lt;br /&gt;
* Anzeige Dateiname während Wiedergabe&lt;br /&gt;
* Pause wird über die Zeitanzeige geschrieben&lt;br /&gt;
* Lyrics werden nicht mehr von Transpose- oder Geschwindigkeitsanzeige gestört&lt;br /&gt;
&lt;br /&gt;
=== Schaltplan ===&lt;br /&gt;
ohne Fernbedienung:&lt;br /&gt;
[[Bild:MrMidi-Circuit.gif|thumb|400px|left|Schaltplan des MIDI-Rekorders 2]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear:both;&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit Fernbedienung:&lt;br /&gt;
[[Bild:MrMidi-Circuit_m168_IR.gif|thumb|400px|left|Schaltplan des MIDI-Rekorders 2 mit Fernbedienung]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br style=&amp;quot;clear:both;&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Achtung: mega168 Pin TX muss an Midi-Buchse OUT Pin 5, und der Pullup muss an Midi-Buchse OUT Pin 4!&lt;br /&gt;
&lt;br /&gt;
Für die Hintergrundbeleuchtung:&lt;br /&gt;
* Port B6+7 parallel an einen Widerstand (hängt von deiner LED-Beleuchtung ab) und diesen dann an LED- deines Displays&lt;br /&gt;
* LED+ vom Display auf VCC 5V&lt;br /&gt;
* Falls das LCD mehr als 40mA zieht, brauchst du einen Transistor!&lt;br /&gt;
* Für Serienproduktion: Verwende einen 8MHz-Quarz an Pin PB6&amp;amp;PB7, siehe Datenblatt. Die Genauigkeit des internen Oszillators ist über die Lebenszeit des Geräts nicht ausreichend - nur für Hobbybastler. Dafür kann dann keine Hintergrundbeleuchtung - falls verwendet - angesteuert werden.&lt;br /&gt;
&lt;br /&gt;
=== Erweiterung um ein echtes MIDI-Wavetable ===&lt;br /&gt;
Besorge dir bei ebay ein Wavetable-Upgrade-Modul (ca.5-10 Euro) mit 26-poligem Anschluss und baue einen Adapter wie hier gezeigt: http://www.heise.de/ct/97/01/328/&lt;br /&gt;
Dazu nimm als +12V-Spannung den 9V-Anschluss der Batterie. Die genannten -12V braucht zumindest mein Creative Pseudo-AWE32-Wavetableplatinchen (nur 53x50mm groß) nicht. Abrunden kannst du das Ganze mit einem Schalter, der als Wavetablequelle den Mr.Midi-Ausgang oder Instrumenten-Eingang wählt. Ein hochwertiger Audioverstärker optimiert das Ergebnis noch.&lt;br /&gt;
Leider sind solche Module recht stromhungrig und nicht immer sehr kompakt (das Yamaha DB-50XG ist ein riesen Teil). Wird Zeit, dass die Handy-Midichips massentauglich werden!&lt;br /&gt;
&lt;br /&gt;
=== Kurzanleitung ===&lt;br /&gt;
* Karte formatieren (FAT16)&lt;br /&gt;
* eventuell Mididateien in Format 0 konvertieren und auf die Karte kopieren&lt;br /&gt;
* Karte einsetzen&lt;br /&gt;
* MrMidi2 anschalten (Scannt die Karte und zeigt erstes File an)&lt;br /&gt;
* Wiedergabe&lt;br /&gt;
** Datei mit UP/DOWN auswählen&lt;br /&gt;
** PLAY zum Abspielen und Pause machen&lt;br /&gt;
** STOP zum Beenden&lt;br /&gt;
** &amp;lt;&amp;lt;/&amp;gt;&amp;gt; um Tempo zu ändern&lt;br /&gt;
** UP/DOWN zum Transponieren&lt;br /&gt;
* Aufnahme&lt;br /&gt;
** REC drücken (Aufnahmebereit)&lt;br /&gt;
** Spielen (Aufnahme beginnt automatisch)&lt;br /&gt;
** erneuter Druck auf REC verwirft die Aufnahme&lt;br /&gt;
** STOP speichert die Aufnahme&lt;br /&gt;
* Letzte Datei löschen&lt;br /&gt;
** &amp;lt;&amp;lt; und &amp;gt;&amp;gt; gleichzeitig drücken (nur im STOP-Mode)&lt;br /&gt;
* Menü&lt;br /&gt;
** UP und DOWN gleichzeitig drücken (nur im STOP-Mode; geht auch, wenn keine Karte eingelegt ist)&lt;br /&gt;
** UP/DOWN zur Auswahl eines Parameters&lt;br /&gt;
** &amp;lt;&amp;lt;/&amp;gt;&amp;gt; zum Ändern&lt;br /&gt;
** Menüpunkte:&lt;br /&gt;
*** 1. Schlagzeugkanal: Default 10 (Das Schlagzeug wird nicht transponiert)&lt;br /&gt;
*** 2. Lyrics anzeigen: Default 1 (1 = Lyrics werden angezeigt)&lt;br /&gt;
*** 3. Startsektor der MMC-Karte: Default 0x0210 (Falls du nur FAT32 auf MMC/SD-Karten hernimmst, kannst du diesen Wert erhöhen)&lt;br /&gt;
*** 4. Mute: Mit &amp;quot;&amp;gt;&amp;gt;&amp;quot; Channel auswählen (Großes Zeichen), mit &amp;quot;&amp;lt;&amp;lt;&amp;quot; Mute toggeln&lt;br /&gt;
*** 5. Repeat: Default 1 - alle Lieder werden der Reihe nach abgespielt, 0 = Stop nach jedem Lied&lt;br /&gt;
*** 6. Backlight&lt;br /&gt;
* Aufnahmen am PC bearbeiten auf FAT16-Dateisystem&lt;br /&gt;
** Karte in PC stecken und Datei öffnen&lt;br /&gt;
* Aufnahmen am PC bearbeiten auf FAT32-Dateisystem (nicht besonders bequem)&lt;br /&gt;
** Karte mit HexEditor laden :-)&lt;br /&gt;
** Ab Sektor Startsektor und aufwärts nach &amp;quot;MThd&amp;quot; suchen lassen&lt;br /&gt;
** Kopiere den ersten Sektor mit &amp;quot;MThd&amp;quot; (10 Bytes) &amp;quot;MTrk&amp;quot; (4 weitere Bytes = Länge Big Endian) und die folgenden Länge/512 Sektoren in eine Datei (mit Endung .mid), löscht die letzten Bytes nach (Länge + 18) Bytes. Diese Datei sollte nun abspielbar und editierbar sein&lt;br /&gt;
* Alte Firmware-Update-Methode&lt;br /&gt;
** PC mit Mr. Midi 2 verbinden (seriell&amp;lt;-&amp;gt;Midi-Konverter)&lt;br /&gt;
** An Mr. Midi 2 beliebige Taste (aber nicht &amp;lt;&amp;lt; oder &amp;gt;&amp;gt;) gedrückt halten und einschalten (Bootloader)&lt;br /&gt;
** flash.bat aufrufen&lt;br /&gt;
&lt;br /&gt;
Erweiterung für IR-Ferbedienung:&lt;br /&gt;
[[Bild:Aldiremote.jpg|thumb|400px|left|ALDI-Universal-Fernbedienung]]&lt;br /&gt;
* RC5-Code, Geräteklasse Videorecorder (wegen der Play/Stop/Rec/Spul-Tasten) :-)&lt;br /&gt;
* Probiert mit Aldi-Fernbedienung aus dem 4. Quartal 2006 mit Gerätecode 257&lt;br /&gt;
* Änderung im Schaltplan: PB2 wird mit einem TSOP1836 verbunden, PB7 mit der Playtaste&lt;br /&gt;
* Tastenbelegung: Play/Rec/Stop/&amp;lt;&amp;lt;/&amp;gt;&amp;gt; wie gewohnt, Menu=Menü, ENT=Löschen, Ziffern für Direktwahl eines Songs. Dabei gilt: Anzahl Songs unter 10 -&amp;gt; Ziffer startet direkt das Lied (0 = Song #10), ansonsten zweistellige Eingabe.&lt;br /&gt;
&lt;br /&gt;
Erweiterung FAT16:&lt;br /&gt;
* Dateinamen werden angezeigt, sogar lange Dateinamen werden erkannt&lt;br /&gt;
* Datei löschen jeder beliebigen Datei, nicht nur die letzte&lt;br /&gt;
* Aufnahmen erzeugen Dateien mit Namen &amp;quot;MM001.MID&amp;quot;, Nummer wird hochgezählt&lt;br /&gt;
* Aufnahmen können im Explorer kopiert und am PC geöffnet werden&lt;br /&gt;
* FAT32-Karten werden im &amp;quot;alten&amp;quot; Modus behandelt (Kein FAT-Eintrag erfolgt, nur immer die letzte Datei wird gelöscht, wenn man Löschen drückt)&lt;br /&gt;
&lt;br /&gt;
Repeatmodi beim Abspielen:&lt;br /&gt;
* 0=Einzeln abspielen und Stopp am Ende des Songs&lt;br /&gt;
* 1=Einzelnen Song wiederholen&lt;br /&gt;
* 2=Alle Songs der Reihe nach abspielen, Stopp nach dem letzten Lied&lt;br /&gt;
* 3=Alle Songs der Reihe nach abspielen, dann von vorne anfangen&lt;br /&gt;
* 4=Zufälliges Lied&lt;br /&gt;
&lt;br /&gt;
=== Build ===&lt;br /&gt;
Projekt kompilieren mit WinAVR 20070525 [http://winavr.sourceforge.net/download.html] + AVR Studio 4.13 [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725]. &lt;br /&gt;
Debuggen mit dem ATAVRDragon über DebugWire.&lt;br /&gt;
&lt;br /&gt;
=== Ausblick ===&lt;br /&gt;
* Vielleicht kommt ja noch irgendwann eine echte FAT16 oder 32 rein (mit ATmega168, da pin-kompatibel). ERLEDIGT für FAT16&lt;br /&gt;
* Volle FAT16-Unterstützung mit fragmentierten Dateien mit Unterordner. ERLEDIGT&lt;br /&gt;
* Sendepuffer, um FAT-Zugriffe während MIDI-Wiedergabe zeitlich unkritisch zu machen. ERLEDIGT&lt;br /&gt;
* Bootloader von MMC/SD-Karte. ERLEDIGT&lt;br /&gt;
* FAT32 vielleicht auch mal. Platz im ATmega168 reicht wohl eher nicht aus, hab gerade noch 3kByte im Flash frei (ohne Bootloader).&lt;br /&gt;
* 32-Bit-Sektoradressierung für große Karten. ERLEDIGT&lt;br /&gt;
&lt;br /&gt;
=== Hinweise ===&lt;br /&gt;
Es handelt sich hier um ein vollständiges privates Bastelprojekt. Es werden keinerlei Funktionsgarantien oder Haftung für Fehler und deren Folgen übernommen. Nachbau und Benutzung erfolgen auf eigene Gefahr.&lt;br /&gt;
&lt;br /&gt;
Zur Kompatibilität von MMC/SD-Karten:&lt;br /&gt;
Meine Versuche haben ergeben, dass die Spannungsteiler mit Widerständen für die Ansteuerung der Karte zu unterschiedlichen Ergebnissen führen: Die eine Karte geht nicht im einen Gerät, dafür im anderen, dafür ist es bei einer anderen Karte genau umgekehrt!&lt;br /&gt;
Die Spannungsversorgung der Karte ist jedoch mittels Spannungsregler auf 3,3V eingestellt.&lt;br /&gt;
Die 16MB-Karte von Canon (Kamera-Beipack) wird vom PC in den FATs anders geschrieben. Warum, weiß ich nicht! Daher sind Aufzeichnungen von MrMidi2 auf diese Karte für den PC nicht lesbar.&lt;br /&gt;
&lt;br /&gt;
Die 128MB-Medion-Karte ist eine SanDisk-Karte (wenn man den Deckel runterbricht, steht das auf den Chips drauf)!&lt;br /&gt;
&lt;br /&gt;
=== Vorschläge ===&lt;br /&gt;
Hier darf jeder Besucher der Seite selber Vorschläge im Rahmen dieses Projektes einbringen (durch Bearbeiten dieses Abschnitts im Wiki):&lt;br /&gt;
* Um Pins für weitere Funktionen zur Verfügung zu haben, wäre es möglich einen 8-Line To 3-Line Priority Encoder (z.B.SN74LS348) für die Taster einzusetzen.&lt;br /&gt;
* Von den freigewordenenen 4 Pins könnte dann einer verwendet werden, um die Beleuchtung der LCD-Anzeige mittels Transistor ein/auszuschalten.&lt;br /&gt;
* Dazu sollten für das LCD-Display die Pins 15 (Vcc Beleuchtung) und Pin 15 (GND Beleuchtung) hinzugefügt werden.&lt;br /&gt;
* Der Quarz könnte somit parallel zur Beleuchtung betrieben werden :-)&lt;br /&gt;
&lt;br /&gt;
=== Quellen (OpenSource) ===&lt;br /&gt;
* [http://www.roboternetz.de/wissen/index.php/RC5-Decoder_f%C3%BCr_ATMega] RC5-Code&lt;br /&gt;
* [http://alpmp3.sourceforge.net/main.php?id=license] Low-Level-Card-Treiber (von mir modifiziert und erweitert)&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD] AVR-LCD-Ansteuerung in Assembler (von mir nach C portiert und erweitert)&lt;br /&gt;
* Der große Rest (FAT-Unterstützung, Midi-Protokoll-Implementierung, Bedienfunktionen) ich (Simon Lehmayr)&lt;br /&gt;
&lt;br /&gt;
=Links=&lt;br /&gt;
* [http://www.lehmayr.de/e_mrmidi.htm] Englischsprachige Beschreibung des Projekts auf meiner Homepage&lt;br /&gt;
* [http://www.sdmidicontroller.com/] Offizieller Hardware-Nachbau als Fertiggerät oder Bausatz (aus England, basiert auf der neuesten MrMidi2-Version mit IR-Remote und MMC/SD-Card-Bootloader). Dieses Gerät wird von mir offiziell unterstützt und es enthält die aktuelle (und bug-bereinigte, feature-strotzende) Firmware. Mein Dank gilt Robert Rushton, der mein kleines Hobby-Projekt professionell vertreibt, für alle, die nicht über die nötigen Fertigkeiten verfügen, so etwas selber zu basteln!&lt;br /&gt;
* [http://www.borg.com/~jglatt/progs/software.htm] Gratis Mididatei-Konverter zwischen Format 0, 1 und 2. Kann auch ganze Ordner konvertieren!&lt;br /&gt;
&lt;br /&gt;
==Forum==&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-90091.html Midirekorder auf Static-RAM]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-102185.html Forumsbeiträge von Mr.Midi 1]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-349932.html Reverse-Piano (sorgt für eine spiegelverkehrte Tastatur)]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-354083.html Mr.MidiPlayer (spielt echte Mididateien von SD/MMC-Karten ab)]&lt;br /&gt;
&lt;br /&gt;
[[Category:Projekte]][[Category:Audio]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:Soft-PWM&amp;diff=25463</id>
		<title>Diskussion:Soft-PWM</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:Soft-PWM&amp;diff=25463"/>
		<updated>2008-01-10T15:43:41Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vielleicht wärs nicht schlecht auch noch drauf hinzuweise, dass man duch ein einfaches bit-reverse die Schaltfrequenz auf die Timer-Zykluszeit hochbringen kann... Damit ist dann das obligatorische Filtern viel einfacher...&lt;br /&gt;
&lt;br /&gt;
Ich hab leider keinen passenden Link gefunden der das illustriert aber mann man ja recht einfach nachprüfen...&lt;br /&gt;
&lt;br /&gt;
Den Zählerwert mit dieser Tabelle &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
static const unsigned char BitReverseTable256[] = &lt;br /&gt;
{&lt;br /&gt;
  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, &lt;br /&gt;
  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, &lt;br /&gt;
  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, &lt;br /&gt;
  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, &lt;br /&gt;
  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, &lt;br /&gt;
  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,&lt;br /&gt;
  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, &lt;br /&gt;
  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,&lt;br /&gt;
  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,&lt;br /&gt;
  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, &lt;br /&gt;
  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,&lt;br /&gt;
  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,&lt;br /&gt;
  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, &lt;br /&gt;
  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,&lt;br /&gt;
  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, &lt;br /&gt;
  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und dann vergleicht man eigentlich BitReverseTable256[ZÄHLERWERT] mit dem eingestellten PWM-Wert...&lt;br /&gt;
&lt;br /&gt;
Wenn man jetzt z.b 50% dutycycle betrachtet erkennt man schon ganz deutlich die Verbesserung:&lt;br /&gt;
bei jedem Vergleich wird getoggelt!&lt;br /&gt;
&lt;br /&gt;
Warum kein einziger Controller den ich kenne sowas macht versteh ich nicht...&lt;br /&gt;
&lt;br /&gt;
Derzeit bastle ich an einer einfachen Motorsteuerung und da wird eben ein DC-Motor per PWM vergewaltigt.&lt;br /&gt;
Software-PWM deshalb weil ich den Timer sowieso zur Zeitmessung verwende..&lt;br /&gt;
Mit dieser Methode hab&#039; ich jetzt eine Schaltfrequenz am Motor von 14kHz :)&lt;br /&gt;
&lt;br /&gt;
73&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;gt;Vielleicht wärs nicht schlecht auch noch drauf hinzuweise, dass man duch ein &amp;gt;einfaches bit-reverse die Schaltfrequenz auf die Timer-Zykluszeit hochbringen &amp;gt;kann... Damit ist dann das obligatorische Filtern viel einfacher...&lt;br /&gt;
&lt;br /&gt;
Das nennt sich Puls-DICHTE-Modulation&lt;br /&gt;
&lt;br /&gt;
&amp;gt;Wenn man jetzt z.b 50% dutycycle betrachtet erkennt man schon ganz deutlich die &amp;gt;Verbesserung:&lt;br /&gt;
&amp;gt;bei jedem Vergleich wird getoggelt!&lt;br /&gt;
&lt;br /&gt;
Fast.&lt;br /&gt;
&lt;br /&gt;
&amp;gt;Warum kein einziger Controller den ich kenne sowas macht versteh ich nicht...&lt;br /&gt;
&lt;br /&gt;
Für PWM/PDM zur Erzeugung von Referenzspannungen für ADC, whatever ist das ganz nützlich. Aber . . .&lt;br /&gt;
&lt;br /&gt;
&amp;gt;Derzeit bastle ich an einer einfachen Motorsteuerung und da wird eben ein &amp;gt;DC-Motor per PWM vergewaltigt.&lt;br /&gt;
&amp;gt;Software-PWM deshalb weil ich den Timer sowieso zur Zeitmessung verwende..&lt;br /&gt;
&amp;gt;Mit dieser Methode hab&#039; ich jetzt eine Schaltfrequenz am Motor von 14kHz :)&lt;br /&gt;
&lt;br /&gt;
Eben für Leistungsendstufen ist das quasi unbrauchbar, weil dann die Schaltverluste zu hoch werden, weil zu oft geschaltet wird.&lt;br /&gt;
&lt;br /&gt;
MFG&lt;br /&gt;
Falk&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Sleep_Mode&amp;diff=25452</id>
		<title>Sleep Mode</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Sleep_Mode&amp;diff=25452"/>
		<updated>2008-01-09T12:08:45Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Überlick über alle Sleep Modes der AVRs */ Tabelle erweitert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Auf deutsch Schlafmodus. Damit bezeichnet man den Zustand eines [[Mikrocontroller]]s, in dem verschiedene Funktionsblöcke ausgeschaltet sind ([[Timer]], [[UART]], [[AD-Wandler]] etc.) um Strom zu sparen. Das ist vor allem in [[Versorgung aus einer Zelle | batteriebetriebenen]] Geräten wichtig. Denn schließlich will man ja ein möglichst lange Laufzeit mit einer möglichst kleinen Batterie erreichen.&lt;br /&gt;
&lt;br /&gt;
Mit dem Sleep Mode eng verknüpft ist [[Ultra low power]] Design. Man muß viele Dinge beachten, um nicht unnötig Strom zu verbrauchen. Z. B. werden in vielen Geräten über lange Zeiträume Daten erfaßt (Datenschreiber). Dabei werden meist relativ wenig Daten in sehr langen Zeitabständen aufgezeichnet und gespeichert. Zwischendurch gibt es für den Mikrocontroller meist nichts zu tun. Dann ist es nicht sinnvoll mit Warteschleifen Zeit zu vertrödeln. Das geht wesentlich stromsparender.&lt;br /&gt;
&lt;br /&gt;
Die Ausführungen dieses Wikiartikels beziehen sich auf den [[AVR]] ATmega32. Prinzipiell gilt das Gesagte auch für viele andere Mikrocontroller ([[MSP430]], [[8051]] etc.), die Details muß man jedoch in den jeweiligen Datenblättern nachlesen. Bei den verschiedenen AVR Modellen findet man diese Informationen im Datenblatt unter der Überschrift &amp;quot;Power Management and Sleep Modes&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Sleep_mode_ablauf.png|framed|Flussdiagramm]] Das Prinzip ist immer gleich. Nach Initialisierung des Mikrocontrollers und eventuell angeschlossener ICs ([[LCD]], [[I2C]]-EEPROM etc.) geht der Prozessor in eine Endlosschleife, in welcher die verschiedenen Aufgaben zyklisch bearbeitet werden. Wenn jedoch gerade nichts zu tun ist geht der Prozessor schlafen. Ein Interrupt weckt ihn, der Interrupt wird ausgeführt und er setzt seine Arbeit in der Hauptschleife fort. Das heißt aber auch, daß vor dem Eintritt in den Sleep-Mode die jeweiligen Interrupts freigegeben werden müssen und auch die entsprechenden Interruptroutinen bereitgestellt werden müssen. Sonst gibt es im wahrsten Sinne des Wortes ein böses Erwachen oder einen endlosen Dornröschenschlaf, der nur durch ein Reset beendet werden kann. Kurz bevor der Mikrocontroller schlafen geht, sollte man möglichst alle Module abschalten, welche nicht gebraucht werden, um wertvollen Strom zu sparen. Dazu zählen&lt;br /&gt;
&lt;br /&gt;
* der Brown Out Detector (per [[AVR Fuses]] einschaltbar)&lt;br /&gt;
* der Analogcomparator&lt;br /&gt;
* interne AD-Referenz&lt;br /&gt;
* Watchdog&lt;br /&gt;
* [[UART]]&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
&lt;br /&gt;
Weitere Hinweise zum Stromsparen gibt es im Artikel [[Ultra low power]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Überlick über alle Sleep Modes der AVRs ==&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Sleep Mode || Verfügbare Module || Stromaufnahme&amp;lt;BR&amp;gt;3,3V/1 MHz || Weckruf || Aufwachzeit&amp;lt;BR&amp;gt;[Takte] || Bemerkung&lt;br /&gt;
|-&lt;br /&gt;
| Active     || alle              &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|  1,2mA                                  || alle     &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0 &lt;br /&gt;
| - &lt;br /&gt;
|- &lt;br /&gt;
| Idle       || alle              &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0,3mA                                   || alle     &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 6 &lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| ADC Noise&amp;lt;BR&amp;gt;Reduction || ADC&amp;lt;BR&amp;gt;Timer 2&amp;lt;BR&amp;gt;ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog  &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| ca. 0,3mA || ADC&amp;lt;BR&amp;gt;Timer 2&amp;lt;BR&amp;gt;ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 6&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Power Save || Timer 2&amp;lt;BR&amp;gt;ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|   10&amp;amp;mu;A         || Timer 2&amp;lt;BR&amp;gt;ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| siehe&amp;lt;BR&amp;gt;[[#Morgenmuffel | hier]]&lt;br /&gt;
| Timer2 mit 32,768 kHz&amp;lt;BR&amp;gt;Uhrenquarz aktiv&lt;br /&gt;
|-&lt;br /&gt;
| Stand By   ||  ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|   35&amp;amp;mu;A                              || ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|  6&lt;br /&gt;
| Hauptoszillator aktiv&lt;br /&gt;
|-&lt;br /&gt;
| Power Down ||  ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0,3&amp;amp;mu;A                                 ||ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| siehe&amp;lt;BR&amp;gt;[[#Morgenmuffel | hier]]&lt;br /&gt;
| -&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die verschiedenen Sleep Modes im Detail ==&lt;br /&gt;
&lt;br /&gt;
=== Active Mode ===&lt;br /&gt;
&lt;br /&gt;
Eigentlich ist das kein Sleep Mode, er soll der Vollständigkeit halber aber genannt werden. In diesem Modus sind alle Takte und alle Module aktiv, zumindest die, die man per Programm aktiviert hat. In diesem Modus ist die CPU ständig am arbeiten, auch wenn es nur eine lange Warteschleife ist. Warten kostet genauso viel Strom wie sinnvolle Dinge berechnen! Dadurch werden auch ständig Daten aus dem [[Speicher#Flash-ROM | Flash]] geladen, nämlich das Programm, welches gerade ausgeführt wird. Das kostet natürlich Strom, wenn gleich auch die Mikrocontroller von heute sehr wenig brauchen. Bei 1 MHz und 3.3V braucht der ATmega ca. 1,2mA, zu finden im Datenblatt unter &amp;quot;ATmega8 Typical Characteristics&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Idle Mode ===&lt;br /&gt;
&lt;br /&gt;
Hier kann zum ersten mal gespart werden. Wenn nichts berechnet werden soll, der Timer, UART, ADC etc. aber arbeiten sollen, dann ist dieser Modus das Mittel der Wahl. Die CPU bleibt stehen, ebenso der Flash, weil ja kein Programm ausgeführt wird. Das spart schon mal einiges, der Stromverbrauch sinkt auf ca. 0,35mA, eine Einsparung von 70%! Aus diesem Modus kann jeder Interrupt die CPU wieder wecken. Sie ist dann bereits nach sechs Takten wieder voll einsatzfähig.&lt;br /&gt;
&lt;br /&gt;
=== ADC Noise Reduction Mode ===&lt;br /&gt;
&lt;br /&gt;
Dieser Modus geht einen kleinen Schritt weiter als der Idle Mode. Hier wird zusätzlich der Takt für die IO-Module abgeschaltet. Nur noch der [[AD-Wandler]], die exteren Interrupts, das [[TWI]] und der Watchdog sind funktionsfähig (wenn man sie nutzen will). Der [[UART]] und Timer0 bzw. Timer1 sind nicht mehr nutzbar. Wie der Name des Sleep Mode vermuten lässt wird das alles getan, um möglichst wenig Störungen mit den internen Modulen zu erzeugen, um die Messung des ADC geringfügig zu verbessern.&lt;br /&gt;
&lt;br /&gt;
=== Power Save Mode ===&lt;br /&gt;
&lt;br /&gt;
Hier werden fast alle Oszillatoren gestoppt. Die einzige Ausnahme ist der Timer 2, welcher asynchron mit einem 32,768 kHz Uhrenquarz betrieben werden kann. Ist er entsprechend konfiguriert, dann bleibt er beim Einschalten des Power Save Mode aktiv. Dieser Modus ist einer der wichtigsten! Da alle Oszillatoren gestoppt sind, funktionieren nur noch die asynchronen Module.&lt;br /&gt;
&lt;br /&gt;
=== Stand By ===&lt;br /&gt;
&lt;br /&gt;
Hier werden alle Takte auf dem IC angehalten. &#039;&#039;&#039;ABER!&#039;&#039;&#039; Der ausgewählte Hauptoszillator läuft weiter. Das kostet zwar etwas Strom, hat aber den Riesenvorteil, daß die CPU nach dem Aufwachen nach nur sechs Takten wieder zur Verfügung seht. Das klingt selbstverständlich, ist es aber nicht! Gerade Quarzoszillatoren haben auf Grund ihrer sehr hohen Güte (sehr schmalbandiger Schwingkreis) eine Einschwingzeit von 10..100ms! Eine kleine Ewigkeit. Dieser Modus ist ideal, wenn ein Quarz verwendet wird und kürzeste Reaktionszeiten nach dem Aufwecken nötig sind. Da alle Takte gestoppt sind, funktionieren nur noch die asynchronen Module.&lt;br /&gt;
&lt;br /&gt;
=== Power Down Mode ===&lt;br /&gt;
&lt;br /&gt;
Das ist der &amp;quot;tiefste&amp;quot; Sleep Mode. Dabei werden &#039;&#039;&#039;ALLE&#039;&#039;&#039; Oszillatoren gestoppt (intern wie extern). Nur noch asynchrone Module funktionieren jetzt noch und können den schlafenden AVR wecken. Die Stromaufnahme wird nur noch von den Leckströmen bestimmt und liegt typisch bei 300nA.&lt;br /&gt;
&lt;br /&gt;
== Morgenmuffel ==&lt;br /&gt;
&lt;br /&gt;
Im Power Save und Power Down Mode ist der Oszillator für die CPU gestoppt. Wird der AVR nun geweckt kann er leider nicht sofort loslegen. Warum? Weil der Oszillator erst anlaufen und sich stabil einschwingen muß. Während dieser Zeit darf die CPU noch nicht loslaufen, sonst kann es passieren, daß sie abstürzt! (Weil ein noch instabiler Oszillator ultrakurze Taktpulse erzeugen kann, an denen sich die CPU &amp;quot;verschluckt&amp;quot;.) Wie bereits oben gesagt kann das Anschwingen eines Quarzoszillators sehr lange dauern. Je niedriger die Frequenz um so länger. Keramikresonatoren sind ca. zehn mal so schnell. Auch das ist ganz einfach physikalisch durch die Güte bestimmt. Sie sind breitbandiger, dadurch zwar ungenauer aber schwingen schneller an. Am schnellsten sind RC-Oszillatoren, leider sind sie aber auch die ungenauesten. Um nun die entsprechende Zeit zu warten wird der Watchdog-Oszillator verwendet. Darum muß man sich nicht direkt kümmern das macht der AVR allein. Aber man muß mittels der [[AVR Fuses]] festlegen, wieviel Takte gewartet werden soll. Die Frequenz des Watchdogoszillators beträgt ca. 1,15 MHz bei 3,3V Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
{| border =&amp;quot;1&amp;quot;&lt;br /&gt;
!   Taktquelle                 || Wartezeit [Takte] || Wartezeit      || typische Einschwingzeit&lt;br /&gt;
|-&lt;br /&gt;
|   externer Quarz (1)         || 16384&amp;lt;BR&amp;gt; 32768      || 14,2ms&amp;lt;BR&amp;gt; 28,4ms || 10ms, Uhrenquarze 100ms&lt;br /&gt;
|-&lt;br /&gt;
|   externer Resonator (1)     || 1024&amp;lt;BR&amp;gt; 16384       || 890&amp;amp;mu;s&amp;lt;BR&amp;gt; 14,2ms || 1ms&lt;br /&gt;
|-&lt;br /&gt;
|   externer Quarzoszillator   || 6                    || 5&amp;amp;mu;s      || Takt liegt konstant an&lt;br /&gt;
|-&lt;br /&gt;
|   externer RC-Oszillator     || 6&amp;lt;BR&amp;gt; 18             || 5&amp;amp;mu;s&amp;lt;BR&amp;gt; 15&amp;amp;mu;s || 1..3 Takte&lt;br /&gt;
|-&lt;br /&gt;
|  interner RC-Oszillator      || 6                    || 5&amp;amp;mu;s        || 1..3 Takte&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(1) Es können für den Quarz bzw. Resonator auch niedigere Wartezeiten verwendet werden. Das ist jedoch nicht empfehlenswert. Näheres siehe Datenblatt.&lt;br /&gt;
&lt;br /&gt;
== Ermittlung des Stromverbrauchs ==&lt;br /&gt;
&lt;br /&gt;
Bei der Messung des Stromverbrauchs haben wir jedoch ein kleines Problem. Die Stromaufnahme eines Mikrocontrollers schwankt bei der Verwendung des Sleep Mode gewaltig. Denn oft wartet der Mikrocontroller im Schlafmodus auf ein Ereignis, z.B. einen Tastendruck. Wird eine Taste gedrückt arbeitet der Mikrocontroller beispielsweise für 100ms und geht dann wieder für Sekunden bis Tage schlafen. Die stoßartige Arbeitsweise (Burst) gleicht einer [[PWM]]. Das Problem ist dabei, daß man theoretisch mit einem Tiefpaß diese PWM filtern (glätten) muß um sie anschließend mit einem Multimeter zu messen. Aber wer will schon einen Tiefpaß mit einer Zeitkonstante von mehreren Minuten bis Tagen bauen und damit messen? Das Problem muß anders gelöst werden. Und das ist recht leicht.&lt;br /&gt;
&lt;br /&gt;
* Mit einem Multimeter mißt man die Stromaufnahme im Sleep Mode (Mikroampere)&lt;br /&gt;
* Mit einem Reihenwiderstand von 10..100 Ohm in der Vcc Leitung des Mikrocontrollers und einem Oszilloskop kann die Stromaufnahme des Mikrocontrollers während der kurzen aktiven Arbeitsphase gemessen werden. Dazu braucht man nur das ohmsche Gesetz, I = U / R. Dabei nutzt man einen 1:1 Tastkopf, um eine maximale Spannungsauflösung zu ereichen. Ausserdem schaltet man den Eingang auf AC-Kopplung, da ja nur Pulse gemessen werden, keine Gleichspannungen. Damit kann man vor allem den Spannungsmessbereich sehr weit runter drehen.  &lt;br /&gt;
* Der mittlere Stromverbrauch wird wie folgt berechnet&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {I_{sleep} \cdot t_{sleep} + I_{aktiv} \cdot t_{aktiv}}{t_{sleep} + t_{aktiv}} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Batterielebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Wie übersetzen sich nun diese Zahlen in eine Batterielebensdauer? Versorgt man den AVR über drei in Reihe geschaltete AAA [[Versorgung aus einer Zelle | Batterien]], kann man etwa folgende Abschätzung machen. Eine AAA Batterie (Mignom) hat eine Kapazität von ca. 1200mAh. D.H. Sie kann für 1200 Stunden (50 Tage) 1mA liefern. Oder für 12000 Stunden 0,1mA, das sind immerhin schon 500 Tage, also über sechzehn Monate! Nutzt man den Power Save Mode (10uA), dann reichen die Batterien bereits 120.000 Stunden, das sind theoretisch 13,7 Jahre! Natürlich sinkt während der Entladung die  Batteriespannung, aber der Mega32 kann ja mit 2,7V betrieben werden, einige [[AVR]]-Typen sogar mit nur 1,8V. Ebenso wird es in 13 Jahren zu einer merklichen Selbstentladung der Batterien kommen, so daß die hier berechnete Zeit nicht erreicht wird. Real kann eher mit drei bis sieben Jahren gerechnet werden. Um eine hohe Lebensdauer zu erreichen müssen dann Lithiumbatterien verwendet werden, welche eine extrem geringe Selbstentladung haben und eine Lebensdauer von 10 Jahren erreichen.&lt;br /&gt;
&lt;br /&gt;
Wenn man wirklich das allerletzte Mikroampere einsparen will und dennoch quarzgenau eine Zeitbasis benötigt, dann verwendet man eine &#039;&#039;&#039;R&#039;&#039;&#039;eal &#039;&#039;&#039;T&#039;&#039;&#039;ime &#039;&#039;&#039;C&#039;&#039;&#039;lock (RTC, Echtzeituhr), wie z.B. [http://datasheets.maxim-ic.com/en/ds/DS1371.pdf DS1371]. Dieser Baustein arbeitet auch mit einem 32,768 kHz Uhrenquarz, ist aber extrem auf Stromsparen optimiert (Stromverbrauch 800..1100nA). Gleichzeitig ist eine Uhr und Weckfunktion eingebaut. Ausgelesen werden kann sie über den [[I2C]]-[[Bus]]. Diese RTC kann den Mikrocontroller periodisch nach einer bestimmten Zeit (Sekunden bis Tage) über einen externen Interrupt wecken. Damit kann der Mikrocontroller in den Power Down Mode gehen und maximal Strom sparen. So eine Kombination aus AVR und RTC verbraucht dann nur noch  ca. 1400nA! Eine kleine 3V Lithiumzelle mit 100mAh reicht dann unglaubliche 71.400 Stunden = 8 Jahre!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allgemein gilt:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Lebensdauer [h] = \frac {Kapazitaet [Ah] }{Strom [A]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, daß diese Formel nur für relativ geringe Entladeströme gilt, typisch bei einer Entladung über 20 Stunden oder länger. Will man hohe Ströme in einer kurzen Zeit entnehmen, sinkt die verfügbare Kapazität. Genaueres findet man dazu [http://www.amplepower.com/pwrnews/beer/ hier] und im Datenblatt der Batterien.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Zur Nutzung des Sleep Mode werden zwei C-Funktionen zur Verfügung gestellt, set_sleep_mode() und sleep_mode(). Genaueres hierzu findet man in der Dokumentation der libc im WINAVR. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== UART Terminal ===&lt;br /&gt;
&lt;br /&gt;
Soll der UART verwendet werden kommt nur der Idle Mode in Frage. In diesem Beispiel wird der AVR genutzt um Zeichen von der [[RS232]] Schnittstelle zu empfangen und per Morsezeichen auszugeben. Als Hauptoszillator wird ein 3,6864 MHz Quarz eingesetzt, wie für eine zuverlässige UART-Kommunikation notwendig ist. Siehe auch [[AVR-Tutorial: UART | AVR-Tutorial]], [[AVR Checkliste#UART.2FUSART | AVR Checkliste]] und [[AVR-GCC-Tutorial#Der_UART | AVR-GCC-Tutorial]]. Im Sleepmode verbraucht der AVR ca. 1,9mA, im aktiven Zustand mit eingeschalteter LED ca. 7,3mA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Idle Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit externem 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400       // Taktfrequenz des Quarzes&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600L          // Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate&lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille &lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// globale Variablen&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t uart_flag;&lt;br /&gt;
volatile uint8_t uart_data;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Ein Byte als &amp;quot;Morsezeichen&amp;quot; auf eine LED ausgeben&lt;br /&gt;
&lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i,j;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)        // Prüfe Bit #0&lt;br /&gt;
            j=250;              // Bit ist 1&lt;br /&gt;
        else&lt;br /&gt;
            j=100;              // Bit ist 0&lt;br /&gt;
&lt;br /&gt;
        PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        long_delay(j);&lt;br /&gt;
        PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus&lt;br /&gt;
        long_delay(1000-j);&lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;             // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
&lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN); &lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_IDLE);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf&lt;br /&gt;
        if (uart_flag==1) {&lt;br /&gt;
            uart_flag =0;&lt;br /&gt;
            morse(uart_data);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    uart_flag = 1;              // signalisiere neue Daten&lt;br /&gt;
    uart_data = UDR;            // Daten auslesen, dadurch wird auch der Interrupt gelöscht&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Quarzgenaue Zeitbasis ===&lt;br /&gt;
&lt;br /&gt;
Hier kommt der Power Save Mode zum Einsatz. Als Zeitbasis dient ein externer 32,768 kHz Uhrenquarz, welcher asynchron den Timer 2 taktet. Das ist zwar nur ein 8 Bit Zähler, der schon nach 256 Takten überläuft, aber es gibt ja zum Glück noch den Vorteiler (Prescaler). Dieser kann die Teilerverhältnisse 1/8/32/256/1024 annehmen, daraus ergeben sich Überlaufzeiten von 7,8125ms/62,5ms/500ms/2s/8s. Werden längere Zeiten benötigt muß man diese in Software nachbilden. Z. B. kann man mit einem zwei Sekunden Intervall in einer Variable bis 30 zählen um exakt eine Minute zu erhalten. Die 30 mal aufwachen, Variable hochzählen und wieder in den Sleep Mode schalten fallen praktisch nicht ins Gewicht, das dauert nur einige Dutzend Mikrosekunden.&lt;br /&gt;
&lt;br /&gt;
Das hier gezeigte Beispiel ist sehr einfach gehalten, zeigt aber deutlich die Vorgehensweise. Im quarzgenauen Zeitraster von einer Minute wird eine LED für zwei Sekunden eingeschaltet. In den realen Anwendungen wird natürliche eine Messung etc. gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Power Save Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator + 32 kHz Quarz&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An TOSC1/TOSC2 muss ein 32,768 kHz Quarz angeschlossen sein&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// globale Variablen&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
&lt;br /&gt;
// Analogcomparator ausschalten&lt;br /&gt;
&lt;br /&gt;
    ACSR = 0x80;&lt;br /&gt;
&lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
&lt;br /&gt;
    ASSR  = (1&amp;lt;&amp;lt; AS2);              // Timer2 asynchron takten&lt;br /&gt;
    long_delay(1000);               // Einschwingzeit des 32kHz Quarzes&lt;br /&gt;
    TCCR2  = 6;                     // Vorteiler 256 -&amp;gt; 2s Überlaufperiode&lt;br /&gt;
    while((ASSR &amp;amp; (1&amp;lt;&amp;lt; TCR2UB)));   // Warte auf das Ende des Zugriffs&lt;br /&gt;
    TIFR  &amp;amp;= ~(1&amp;lt;&amp;lt;TOV2);            // Interrupts löschen&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer overflow Interrupt freischalten&lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
&lt;br /&gt;
        // WICHTIG!&lt;br /&gt;
        // Wenn der Timer2 im asynchronen Modus periodisch zum Wecken aus dem Sleep Mode&lt;br /&gt;
        // genutzt wird, dann muss vor dem Wiedereintritt mindestens&lt;br /&gt;
        // 1 Oszillatortakt des Timers abgewartet werden (~30us), um die Interruptlogik &lt;br /&gt;
        // wieder zu aktivieren, anderenfalls wacht der AVR nicht mehr auf.&lt;br /&gt;
        // Die folgenden zwei Zeilen tun dies.&lt;br /&gt;
        // Nur wenn sichergestellt ist, dass der Interrupt + Hauptschleife länger als 30us dauern,&lt;br /&gt;
        // kann man den Test weglassen&lt;br /&gt;
&lt;br /&gt;
        OCR2 = 0;                       // Dummyzugriff&lt;br /&gt;
        while((ASSR &amp;amp; (1&amp;lt;&amp;lt; OCR2UB)));   // Warte auf das Ende des Zugriffs&lt;br /&gt;
&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_PWR_SAVE);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf, nach Ausführung des Interupts&lt;br /&gt;
&lt;br /&gt;
        if (flag==1) {                  // Neues Messintervall ?&lt;br /&gt;
            flag =0;&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);        // LED für eine Sekunde anschalten&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_OVF_vect) {&lt;br /&gt;
    static uint8_t ticks;               // Hilfsvariable für Messintervall&lt;br /&gt;
&lt;br /&gt;
    ticks++;&lt;br /&gt;
    if (ticks==30) {                    // neues Messintervall ?&lt;br /&gt;
        ticks=0;&lt;br /&gt;
        flag=1;&lt;br /&gt;
    }&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);               // LED ausschalten&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Sleep Mode beträgt die Stromaufnahme des AVR ca. 10&amp;amp;mu;A, während der zwei Sekunden LED Leuchtdauer 2,3mA. Dadurch ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {10 \mu A \cdot 58s + 2,3mA \cdot 2s}{60s} = 86,3 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Korrekterweise müßte man jedoch noch den Stromverbrauch berücksichtigen, welcher bei jedem Timerüberlauf alle zwei Sekunden entsteht. Dabei wird einmal der Timerinterrupt durchlaufen sowie einmal die Hauptschleife. Das dauert hier ca. 50 Takte, bei 1 MHz somit 50&amp;amp;mu;s. Zusätzlich wird noch bis zu 30us gewartet, um garantiert einen Takt im Timer 2 vergehen zu lassen. Das passiert pro Minute 30 mal, somit erhöht sich der mittlere Stromverbrauch um&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac {30 \cdot 80 \mu s \cdot 1,2mA}{60s}= 48nA&amp;lt;/math&amp;gt; , was somit praktisch vollkommen vernachlässigbar ist.&lt;br /&gt;
&lt;br /&gt;
=== Aufwachen per Tastendruck ===&lt;br /&gt;
&lt;br /&gt;
Maximale Einsparung an Energie bringt der Power Down Modus. Das Aufwecken muß dann aber durch eine levelsensitiven externen Interrupt erfolgen. Ein flankensensitiver Interrupt ist nicht möglich! Auch hier blinkt eine LED.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Power Down Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD2 muss ein Taster gegen GND angeschlossen sein&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFB;        // PD2 = INT0 = Eingang&lt;br /&gt;
    PORTD = 0x04;       // Pull Up aktivieren&lt;br /&gt;
&lt;br /&gt;
// Analogcomparator ausschalten&lt;br /&gt;
&lt;br /&gt;
    ACSR = 0x80;&lt;br /&gt;
&lt;br /&gt;
// Interrupt konfigurieren&lt;br /&gt;
&lt;br /&gt;
    MCUCR &amp;amp;= ~0x3;              // levelgesteuerter Interrupt an INT0&lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
&lt;br /&gt;
        GICR |= (1 &amp;lt;&amp;lt; INT0);            // externen Interrupt freigeben&lt;br /&gt;
&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf&lt;br /&gt;
        GICR &amp;amp;= ~(1 &amp;lt;&amp;lt; INT0);           // externen Interrupt sperren&lt;br /&gt;
                                        // WICHTIG! falls der externe LOW Puls an INT0&lt;br /&gt;
                                        // sehr lange dauert&lt;br /&gt;
&lt;br /&gt;
        PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED für eine Sekunde anschalten&lt;br /&gt;
        long_delay(1000);&lt;br /&gt;
        PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// externer Interrupt INT0 &lt;br /&gt;
&lt;br /&gt;
// Die Interruptroutine kann leer sein, ABER sie muss existieren!&lt;br /&gt;
// Sonst springt der AVR nach dem Aufwachen zum Reset, weil kein sinnvoller&lt;br /&gt;
// Interruptvektor eingetragen ist!&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Power Down Mode beträgt die Stromaufnahme des AVR weniger als 1&amp;amp;mu;A (weniger konnte mit dem verfügbaren Messgerät nicht gemessen werden), laut Datenblatt ca. 0,3&amp;amp;mu;A. Während der einen Sekunde LED Leuchtdauer werden 3,8mA verbraucht. Wird einmal pro Minute die Taste gedrückt, ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {0,3 \mu A \cdot 59s + 3,8mA \cdot 1s}{60s} = 63,6 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird nur einmal pro Stunde die Taste gedrückt, ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {0,3 \mu A \cdot 3559s + 3,8mA \cdot 1s}{3660s} = 1,3 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* [http://datasheets.maxim-ic.com/en/ds/DS1371.pdf DS1371], Echtzeituhr mit 1&amp;amp;mu;A Stromverbrauch&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* TODO&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Sleep_Mode&amp;diff=25451</id>
		<title>Sleep Mode</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Sleep_Mode&amp;diff=25451"/>
		<updated>2008-01-09T11:37:06Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Auf deutsch Schlafmodus. Damit bezeichnet man den Zustand eines [[Mikrocontroller]]s, in dem verschiedene Funktionsblöcke ausgeschaltet sind ([[Timer]], [[UART]], [[AD-Wandler]] etc.) um Strom zu sparen. Das ist vor allem in [[Versorgung aus einer Zelle | batteriebetriebenen]] Geräten wichtig. Denn schließlich will man ja ein möglichst lange Laufzeit mit einer möglichst kleinen Batterie erreichen.&lt;br /&gt;
&lt;br /&gt;
Mit dem Sleep Mode eng verknüpft ist [[Ultra low power]] Design. Man muß viele Dinge beachten, um nicht unnötig Strom zu verbrauchen. Z. B. werden in vielen Geräten über lange Zeiträume Daten erfaßt (Datenschreiber). Dabei werden meist relativ wenig Daten in sehr langen Zeitabständen aufgezeichnet und gespeichert. Zwischendurch gibt es für den Mikrocontroller meist nichts zu tun. Dann ist es nicht sinnvoll mit Warteschleifen Zeit zu vertrödeln. Das geht wesentlich stromsparender.&lt;br /&gt;
&lt;br /&gt;
Die Ausführungen dieses Wikiartikels beziehen sich auf den [[AVR]] ATmega32. Prinzipiell gilt das Gesagte auch für viele andere Mikrocontroller ([[MSP430]], [[8051]] etc.), die Details muß man jedoch in den jeweiligen Datenblättern nachlesen. Bei den verschiedenen AVR Modellen findet man diese Informationen im Datenblatt unter der Überschrift &amp;quot;Power Management and Sleep Modes&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
[[Bild:Sleep_mode_ablauf.png|framed|Flussdiagramm]] Das Prinzip ist immer gleich. Nach Initialisierung des Mikrocontrollers und eventuell angeschlossener ICs ([[LCD]], [[I2C]]-EEPROM etc.) geht der Prozessor in eine Endlosschleife, in welcher die verschiedenen Aufgaben zyklisch bearbeitet werden. Wenn jedoch gerade nichts zu tun ist geht der Prozessor schlafen. Ein Interrupt weckt ihn, der Interrupt wird ausgeführt und er setzt seine Arbeit in der Hauptschleife fort. Das heißt aber auch, daß vor dem Eintritt in den Sleep-Mode die jeweiligen Interrupts freigegeben werden müssen und auch die entsprechenden Interruptroutinen bereitgestellt werden müssen. Sonst gibt es im wahrsten Sinne des Wortes ein böses Erwachen oder einen endlosen Dornröschenschlaf, der nur durch ein Reset beendet werden kann. Kurz bevor der Mikrocontroller schlafen geht, sollte man möglichst alle Module abschalten, welche nicht gebraucht werden, um wertvollen Strom zu sparen. Dazu zählen&lt;br /&gt;
&lt;br /&gt;
* der Brown Out Detector (per [[AVR Fuses]] einschaltbar)&lt;br /&gt;
* der Analogcomparator&lt;br /&gt;
* interne AD-Referenz&lt;br /&gt;
* Watchdog&lt;br /&gt;
* [[UART]]&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
&lt;br /&gt;
Weitere Hinweise zum Stromsparen gibt es im Artikel [[Ultra low power]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Überlick über alle Sleep Modes der AVRs ==&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Sleep Mode || Verfügbare Module || Stromaufnahme&amp;lt;BR&amp;gt;3,3V/1 MHz || Weckruf || Aufwachzeit&amp;lt;BR&amp;gt;[Takte]&lt;br /&gt;
|-&lt;br /&gt;
| Active     || alle              &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|  1,2mA                                  || alle     &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0  &lt;br /&gt;
|- &lt;br /&gt;
| Idle       || alle              &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0,3mA                                   || alle     &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 6 &lt;br /&gt;
|-&lt;br /&gt;
| ADC Noise&amp;lt;BR&amp;gt;Reduction || ADC&amp;lt;BR&amp;gt;Timer 2&amp;lt;BR&amp;gt;ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog  &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| ca. 0,3mA || ADC&amp;lt;BR&amp;gt;Timer 2&amp;lt;BR&amp;gt;ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 6&lt;br /&gt;
|-&lt;br /&gt;
| Power Save || Timer 2&amp;lt;BR&amp;gt;ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|   10&amp;amp;mu;A         || Timer 2&amp;lt;BR&amp;gt;ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| siehe&amp;lt;BR&amp;gt;[[#Morgenmuffel | hier]]&lt;br /&gt;
|-&lt;br /&gt;
| Stand By   ||  ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|   35&amp;amp;mu;A                              || ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;|  6       &lt;br /&gt;
|-&lt;br /&gt;
| Power Down ||  ext. Interrupts&amp;lt;BR&amp;gt;TWI&amp;lt;BR&amp;gt;Watchdog &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| 0,3&amp;amp;mu;A                                 ||ext. &#039;&#039;&#039;level&#039;&#039;&#039; Interrupt&amp;lt;BR&amp;gt;TWI Address match&amp;lt;BR&amp;gt;Brown Out Detector Reset&amp;lt;BR&amp;gt;External Reset&amp;lt;BR&amp;gt;Watchdog Reset &lt;br /&gt;
|align =&amp;quot;center&amp;quot;| siehe&amp;lt;BR&amp;gt;[[#Morgenmuffel | hier]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Die verschiedenen Sleep Modes im Detail ==&lt;br /&gt;
&lt;br /&gt;
=== Active Mode ===&lt;br /&gt;
&lt;br /&gt;
Eigentlich ist das kein Sleep Mode, er soll der Vollständigkeit halber aber genannt werden. In diesem Modus sind alle Takte und alle Module aktiv, zumindest die, die man per Programm aktiviert hat. In diesem Modus ist die CPU ständig am arbeiten, auch wenn es nur eine lange Warteschleife ist. Warten kostet genauso viel Strom wie sinnvolle Dinge berechnen! Dadurch werden auch ständig Daten aus dem [[Speicher#Flash-ROM | Flash]] geladen, nämlich das Programm, welches gerade ausgeführt wird. Das kostet natürlich Strom, wenn gleich auch die Mikrocontroller von heute sehr wenig brauchen. Bei 1 MHz und 3.3V braucht der ATmega ca. 1,2mA, zu finden im Datenblatt unter &amp;quot;ATmega8 Typical Characteristics&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Idle Mode ===&lt;br /&gt;
&lt;br /&gt;
Hier kann zum ersten mal gespart werden. Wenn nichts berechnet werden soll, der Timer, UART, ADC etc. aber arbeiten sollen, dann ist dieser Modus das Mittel der Wahl. Die CPU bleibt stehen, ebenso der Flash, weil ja kein Programm ausgeführt wird. Das spart schon mal einiges, der Stromverbrauch sinkt auf ca. 0,35mA, eine Einsparung von 70%! Aus diesem Modus kann jeder Interrupt die CPU wieder wecken. Sie ist dann bereits nach sechs Takten wieder voll einsatzfähig.&lt;br /&gt;
&lt;br /&gt;
=== ADC Noise Reduction Mode ===&lt;br /&gt;
&lt;br /&gt;
Dieser Modus geht einen kleinen Schritt weiter als der Idle Mode. Hier wird zusätzlich der Takt für die IO-Module abgeschaltet. Nur noch der [[AD-Wandler]], die exteren Interrupts, das [[TWI]] und der Watchdog sind funktionsfähig (wenn man sie nutzen will). Der [[UART]] und Timer0 bzw. Timer1 sind nicht mehr nutzbar. Wie der Name des Sleep Mode vermuten lässt wird das alles getan, um möglichst wenig Störungen mit den internen Modulen zu erzeugen, um die Messung des ADC geringfügig zu verbessern.&lt;br /&gt;
&lt;br /&gt;
=== Power Save Mode ===&lt;br /&gt;
&lt;br /&gt;
Hier werden fast alle Oszillatoren gestoppt. Die einzige Ausnahme ist der Timer 2, welcher asynchron mit einem 32,768 kHz Uhrenquarz betrieben werden kann. Ist er entsprechend konfiguriert, dann bleibt er beim Einschalten des Power Save Mode aktiv. Dieser Modus ist einer der wichtigsten! Da alle Oszillatoren gestoppt sind, funktionieren nur noch die asynchronen Module.&lt;br /&gt;
&lt;br /&gt;
=== Stand By ===&lt;br /&gt;
&lt;br /&gt;
Hier werden alle Takte auf dem IC angehalten. &#039;&#039;&#039;ABER!&#039;&#039;&#039; Der ausgewählte Hauptoszillator läuft weiter. Das kostet zwar etwas Strom, hat aber den Riesenvorteil, daß die CPU nach dem Aufwachen nach nur sechs Takten wieder zur Verfügung seht. Das klingt selbstverständlich, ist es aber nicht! Gerade Quarzoszillatoren haben auf Grund ihrer sehr hohen Güte (sehr schmalbandiger Schwingkreis) eine Einschwingzeit von 10..100ms! Eine kleine Ewigkeit. Dieser Modus ist ideal, wenn ein Quarz verwendet wird und kürzeste Reaktionszeiten nach dem Aufwecken nötig sind. Da alle Takte gestoppt sind, funktionieren nur noch die asynchronen Module.&lt;br /&gt;
&lt;br /&gt;
=== Power Down Mode ===&lt;br /&gt;
&lt;br /&gt;
Das ist der &amp;quot;tiefste&amp;quot; Sleep Mode. Dabei werden &#039;&#039;&#039;ALLE&#039;&#039;&#039; Oszillatoren gestoppt (intern wie extern). Nur noch asynchrone Module funktionieren jetzt noch und können den schlafenden AVR wecken. Die Stromaufnahme wird nur noch von den Leckströmen bestimmt und liegt typisch bei 300nA.&lt;br /&gt;
&lt;br /&gt;
== Morgenmuffel ==&lt;br /&gt;
&lt;br /&gt;
Im Power Save und Power Down Mode ist der Oszillator für die CPU gestoppt. Wird der AVR nun geweckt kann er leider nicht sofort loslegen. Warum? Weil der Oszillator erst anlaufen und sich stabil einschwingen muß. Während dieser Zeit darf die CPU noch nicht loslaufen, sonst kann es passieren, daß sie abstürzt! (Weil ein noch instabiler Oszillator ultrakurze Taktpulse erzeugen kann, an denen sich die CPU &amp;quot;verschluckt&amp;quot;.) Wie bereits oben gesagt kann das Anschwingen eines Quarzoszillators sehr lange dauern. Je niedriger die Frequenz um so länger. Keramikresonatoren sind ca. zehn mal so schnell. Auch das ist ganz einfach physikalisch durch die Güte bestimmt. Sie sind breitbandiger, dadurch zwar ungenauer aber schwingen schneller an. Am schnellsten sind RC-Oszillatoren, leider sind sie aber auch die ungenauesten. Um nun die entsprechende Zeit zu warten wird der Watchdog-Oszillator verwendet. Darum muß man sich nicht direkt kümmern das macht der AVR allein. Aber man muß mittels der [[AVR Fuses]] festlegen, wieviel Takte gewartet werden soll. Die Frequenz des Watchdogoszillators beträgt ca. 1,15 MHz bei 3,3V Versorgungsspannung.&lt;br /&gt;
&lt;br /&gt;
{| border =&amp;quot;1&amp;quot;&lt;br /&gt;
!   Taktquelle                 || Wartezeit [Takte] || Wartezeit      || typische Einschwingzeit&lt;br /&gt;
|-&lt;br /&gt;
|   externer Quarz (1)         || 16384&amp;lt;BR&amp;gt; 32768      || 14,2ms&amp;lt;BR&amp;gt; 28,4ms || 10ms, Uhrenquarze 100ms&lt;br /&gt;
|-&lt;br /&gt;
|   externer Resonator (1)     || 1024&amp;lt;BR&amp;gt; 16384       || 890&amp;amp;mu;s&amp;lt;BR&amp;gt; 14,2ms || 1ms&lt;br /&gt;
|-&lt;br /&gt;
|   externer Quarzoszillator   || 6                    || 5&amp;amp;mu;s      || Takt liegt konstant an&lt;br /&gt;
|-&lt;br /&gt;
|   externer RC-Oszillator     || 6&amp;lt;BR&amp;gt; 18             || 5&amp;amp;mu;s&amp;lt;BR&amp;gt; 15&amp;amp;mu;s || 1..3 Takte&lt;br /&gt;
|-&lt;br /&gt;
|  interner RC-Oszillator      || 6                    || 5&amp;amp;mu;s        || 1..3 Takte&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
(1) Es können für den Quarz bzw. Resonator auch niedigere Wartezeiten verwendet werden. Das ist jedoch nicht empfehlenswert. Näheres siehe Datenblatt.&lt;br /&gt;
&lt;br /&gt;
== Ermittlung des Stromverbrauchs ==&lt;br /&gt;
&lt;br /&gt;
Bei der Messung des Stromverbrauchs haben wir jedoch ein kleines Problem. Die Stromaufnahme eines Mikrocontrollers schwankt bei der Verwendung des Sleep Mode gewaltig. Denn oft wartet der Mikrocontroller im Schlafmodus auf ein Ereignis, z.B. einen Tastendruck. Wird eine Taste gedrückt arbeitet der Mikrocontroller beispielsweise für 100ms und geht dann wieder für Sekunden bis Tage schlafen. Die stoßartige Arbeitsweise (Burst) gleicht einer [[PWM]]. Das Problem ist dabei, daß man theoretisch mit einem Tiefpaß diese PWM filtern (glätten) muß um sie anschließend mit einem Multimeter zu messen. Aber wer will schon einen Tiefpaß mit einer Zeitkonstante von mehreren Minuten bis Tagen bauen und damit messen? Das Problem muß anders gelöst werden. Und das ist recht leicht.&lt;br /&gt;
&lt;br /&gt;
* Mit einem Multimeter mißt man die Stromaufnahme im Sleep Mode (Mikroampere)&lt;br /&gt;
* Mit einem Reihenwiderstand von 10..100 Ohm in der Vcc Leitung des Mikrocontrollers und einem Oszilloskop kann die Stromaufnahme des Mikrocontrollers während der kurzen aktiven Arbeitsphase gemessen werden. Dazu braucht man nur das ohmsche Gesetz, I = U / R. Dabei nutzt man einen 1:1 Tastkopf, um eine maximale Spannungsauflösung zu ereichen. Ausserdem schaltet man den Eingang auf AC-Kopplung, da ja nur Pulse gemessen werden, keine Gleichspannungen. Damit kann man vor allem den Spannungsmessbereich sehr weit runter drehen.  &lt;br /&gt;
* Der mittlere Stromverbrauch wird wie folgt berechnet&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {I_{sleep} \cdot t_{sleep} + I_{aktiv} \cdot t_{aktiv}}{t_{sleep} + t_{aktiv}} &amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Batterielebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Wie übersetzen sich nun diese Zahlen in eine Batterielebensdauer? Versorgt man den AVR über drei in Reihe geschaltete AAA [[Versorgung aus einer Zelle | Batterien]], kann man etwa folgende Abschätzung machen. Eine AAA Batterie (Mignom) hat eine Kapazität von ca. 1200mAh. D.H. Sie kann für 1200 Stunden (50 Tage) 1mA liefern. Oder für 12000 Stunden 0,1mA, das sind immerhin schon 500 Tage, also über sechzehn Monate! Nutzt man den Power Save Mode (10uA), dann reichen die Batterien bereits 120.000 Stunden, das sind theoretisch 13,7 Jahre! Natürlich sinkt während der Entladung die  Batteriespannung, aber der Mega32 kann ja mit 2,7V betrieben werden, einige [[AVR]]-Typen sogar mit nur 1,8V. Ebenso wird es in 13 Jahren zu einer merklichen Selbstentladung der Batterien kommen, so daß die hier berechnete Zeit nicht erreicht wird. Real kann eher mit drei bis sieben Jahren gerechnet werden. Um eine hohe Lebensdauer zu erreichen müssen dann Lithiumbatterien verwendet werden, welche eine extrem geringe Selbstentladung haben und eine Lebensdauer von 10 Jahren erreichen.&lt;br /&gt;
&lt;br /&gt;
Wenn man wirklich das allerletzte Mikroampere einsparen will und dennoch quarzgenau eine Zeitbasis benötigt, dann verwendet man eine &#039;&#039;&#039;R&#039;&#039;&#039;eal &#039;&#039;&#039;T&#039;&#039;&#039;ime &#039;&#039;&#039;C&#039;&#039;&#039;lock (RTC, Echtzeituhr), wie z.B. [http://datasheets.maxim-ic.com/en/ds/DS1371.pdf DS1371]. Dieser Baustein arbeitet auch mit einem 32,768 kHz Uhrenquarz, ist aber extrem auf Stromsparen optimiert (Stromverbrauch 800..1100nA). Gleichzeitig ist eine Uhr und Weckfunktion eingebaut. Ausgelesen werden kann sie über den [[I2C]]-[[Bus]]. Diese RTC kann den Mikrocontroller periodisch nach einer bestimmten Zeit (Sekunden bis Tage) über einen externen Interrupt wecken. Damit kann der Mikrocontroller in den Power Down Mode gehen und maximal Strom sparen. So eine Kombination aus AVR und RTC verbraucht dann nur noch  ca. 1400nA! Eine kleine 3V Lithiumzelle mit 100mAh reicht dann unglaubliche 71.400 Stunden = 8 Jahre!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allgemein gilt:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;Lebensdauer [h] = \frac {Kapazitaet [Ah] }{Strom [A]}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, daß diese Formel nur für relativ geringe Entladeströme gilt, typisch bei einer Entladung über 20 Stunden oder länger. Will man hohe Ströme in einer kurzen Zeit entnehmen, sinkt die verfügbare Kapazität. Genaueres findet man dazu [http://www.amplepower.com/pwrnews/beer/ hier] und im Datenblatt der Batterien.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Zur Nutzung des Sleep Mode werden zwei C-Funktionen zur Verfügung gestellt, set_sleep_mode() und sleep_mode(). Genaueres hierzu findet man in der Dokumentation der libc im WINAVR. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== UART Terminal ===&lt;br /&gt;
&lt;br /&gt;
Soll der UART verwendet werden kommt nur der Idle Mode in Frage. In diesem Beispiel wird der AVR genutzt um Zeichen von der [[RS232]] Schnittstelle zu empfangen und per Morsezeichen auszugeben. Als Hauptoszillator wird ein 3,6864 MHz Quarz eingesetzt, wie für eine zuverlässige UART-Kommunikation notwendig ist. Siehe auch [[AVR-Tutorial: UART | AVR-Tutorial]], [[AVR Checkliste#UART.2FUSART | AVR Checkliste]] und [[AVR-GCC-Tutorial#Der_UART | AVR-GCC-Tutorial]]. Im Sleepmode verbraucht der AVR ca. 1,9mA, im aktiven Zustand mit eingeschalteter LED ca. 7,3mA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Idle Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit externem 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 3686400       // Taktfrequenz des Quarzes&lt;br /&gt;
&lt;br /&gt;
#define BAUD 9600L          // Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate&lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille &lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// globale Variablen&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t uart_flag;&lt;br /&gt;
volatile uint8_t uart_data;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Ein Byte als &amp;quot;Morsezeichen&amp;quot; auf eine LED ausgeben&lt;br /&gt;
&lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i,j;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)        // Prüfe Bit #0&lt;br /&gt;
            j=250;              // Bit ist 1&lt;br /&gt;
        else&lt;br /&gt;
            j=100;              // Bit ist 0&lt;br /&gt;
&lt;br /&gt;
        PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        long_delay(j);&lt;br /&gt;
        PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus&lt;br /&gt;
        long_delay(1000-j);&lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;             // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
&lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
&lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN); &lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_IDLE);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf&lt;br /&gt;
        if (uart_flag==1) {&lt;br /&gt;
            uart_flag =0;&lt;br /&gt;
            morse(uart_data);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
&lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    uart_flag = 1;              // signalisiere neue Daten&lt;br /&gt;
    uart_data = UDR;            // Daten auslesen, dadurch wird auch der Interrupt gelöscht&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Quarzgenaue Zeitbasis ===&lt;br /&gt;
&lt;br /&gt;
Hier kommt der Power Save Mode zum Einsatz. Als Zeitbasis dient ein externer 32,768 kHz Uhrenquarz, welcher asynchron den Timer 2 taktet. Das ist zwar nur ein 8 Bit Zähler, der schon nach 256 Takten überläuft, aber es gibt ja zum Glück noch den Vorteiler (Prescaler). Dieser kann die Teilerverhältnisse 1/8/32/256/1024 annehmen, daraus ergeben sich Überlaufzeiten von 7,8125ms/62,5ms/500ms/2s/8s. Werden längere Zeiten benötigt muß man diese in Software nachbilden. Z. B. kann man mit einem zwei Sekunden Intervall in einer Variable bis 30 zählen um exakt eine Minute zu erhalten. Die 30 mal aufwachen, Variable hochzählen und wieder in den Sleep Mode schalten fallen praktisch nicht ins Gewicht, das dauert nur einige Dutzend Mikrosekunden.&lt;br /&gt;
&lt;br /&gt;
Das hier gezeigte Beispiel ist sehr einfach gehalten, zeigt aber deutlich die Vorgehensweise. Im quarzgenauen Zeitraster von einer Minute wird eine LED für zwei Sekunden eingeschaltet. In den realen Anwendungen wird natürliche eine Messung etc. gemacht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Power Save Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator + 32 kHz Quarz&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An TOSC1/TOSC2 muss ein 32,768 kHz Quarz angeschlossen sein&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// globale Variablen&lt;br /&gt;
&lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
&lt;br /&gt;
// Analogcomparator ausschalten&lt;br /&gt;
&lt;br /&gt;
    ACSR = 0x80;&lt;br /&gt;
&lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
&lt;br /&gt;
    ASSR  = (1&amp;lt;&amp;lt; AS2);              // Timer2 asynchron takten&lt;br /&gt;
    long_delay(1000);               // Einschwingzeit des 32kHz Quarzes&lt;br /&gt;
    TCCR2  = 6;                     // Vorteiler 256 -&amp;gt; 2s Überlaufperiode&lt;br /&gt;
    while((ASSR &amp;amp; (1&amp;lt;&amp;lt; TCR2UB)));   // Warte auf das Ende des Zugriffs&lt;br /&gt;
    TIFR  &amp;amp;= ~(1&amp;lt;&amp;lt;TOV2);            // Interrupts löschen&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer overflow Interrupt freischalten&lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
&lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
&lt;br /&gt;
        // WICHTIG!&lt;br /&gt;
        // Wenn der Timer2 im asynchronen Modus periodisch zum Wecken aus dem Sleep Mode&lt;br /&gt;
        // genutzt wird, dann muss vor dem Wiedereintritt mindestens&lt;br /&gt;
        // 1 Oszillatortakt des Timers abgewartet werden (~30us), um die Interruptlogik &lt;br /&gt;
        // wieder zu aktivieren, anderenfalls wacht der AVR nicht mehr auf.&lt;br /&gt;
        // Die folgenden zwei Zeilen tun dies.&lt;br /&gt;
        // Nur wenn sichergestellt ist, dass der Interrupt + Hauptschleife länger als 30us dauern,&lt;br /&gt;
        // kann man den Test weglassen&lt;br /&gt;
&lt;br /&gt;
        OCR2 = 0;                       // Dummyzugriff&lt;br /&gt;
        while((ASSR &amp;amp; (1&amp;lt;&amp;lt; OCR2UB)));   // Warte auf das Ende des Zugriffs&lt;br /&gt;
&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_PWR_SAVE);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf, nach Ausführung des Interupts&lt;br /&gt;
&lt;br /&gt;
        if (flag==1) {                  // Neues Messintervall ?&lt;br /&gt;
            flag =0;&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);        // LED für eine Sekunde anschalten&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER2_OVF_vect) {&lt;br /&gt;
    static uint8_t ticks;               // Hilfsvariable für Messintervall&lt;br /&gt;
&lt;br /&gt;
    ticks++;&lt;br /&gt;
    if (ticks==30) {                    // neues Messintervall ?&lt;br /&gt;
        ticks=0;&lt;br /&gt;
        flag=1;&lt;br /&gt;
    }&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);               // LED ausschalten&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Sleep Mode beträgt die Stromaufnahme des AVR ca. 10&amp;amp;mu;A, während der zwei Sekunden LED Leuchtdauer 2,3mA. Dadurch ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {10 \mu A \cdot 58s + 2,3mA \cdot 2s}{60s} = 86,3 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Korrekterweise müßte man jedoch noch den Stromverbrauch berücksichtigen, welcher bei jedem Timerüberlauf alle zwei Sekunden entsteht. Dabei wird einmal der Timerinterrupt durchlaufen sowie einmal die Hauptschleife. Das dauert hier ca. 50 Takte, bei 1 MHz somit 50&amp;amp;mu;s. Zusätzlich wird noch bis zu 30us gewartet, um garantiert einen Takt im Timer 2 vergehen zu lassen. Das passiert pro Minute 30 mal, somit erhöht sich der mittlere Stromverbrauch um&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;\frac {30 \cdot 80 \mu s \cdot 1,2mA}{60s}= 48nA&amp;lt;/math&amp;gt; , was somit praktisch vollkommen vernachlässigbar ist.&lt;br /&gt;
&lt;br /&gt;
=== Aufwachen per Tastendruck ===&lt;br /&gt;
&lt;br /&gt;
Maximale Einsparung an Energie bringt der Power Down Modus. Das Aufwecken muß dann aber durch eine levelsensitiven externen Interrupt erfolgen. Ein flankensensitiver Interrupt ist nicht möglich! Auch hier blinkt eine LED.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Sleep Mode Demo, Power Down Mode&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD2 muss ein Taster gegen GND angeschlossen sein&lt;br /&gt;
*&lt;br /&gt;
************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/sleep.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main (void) {&lt;br /&gt;
&lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
&lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFB;        // PD2 = INT0 = Eingang&lt;br /&gt;
    PORTD = 0x04;       // Pull Up aktivieren&lt;br /&gt;
&lt;br /&gt;
// Analogcomparator ausschalten&lt;br /&gt;
&lt;br /&gt;
    ACSR = 0x80;&lt;br /&gt;
&lt;br /&gt;
// Interrupt konfigurieren&lt;br /&gt;
&lt;br /&gt;
    MCUCR &amp;amp;= ~0x3;              // levelgesteuerter Interrupt an INT0&lt;br /&gt;
&lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
&lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
&lt;br /&gt;
    while(1) {&lt;br /&gt;
&lt;br /&gt;
        GICR |= (1 &amp;lt;&amp;lt; INT0);            // externen Interrupt freigeben&lt;br /&gt;
&lt;br /&gt;
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);&lt;br /&gt;
        sleep_mode();                   // in den Schlafmodus wechseln&lt;br /&gt;
&lt;br /&gt;
        // hier wachen wir wieder auf&lt;br /&gt;
        GICR &amp;amp;= ~(1 &amp;lt;&amp;lt; INT0);           // externen Interrupt sperren&lt;br /&gt;
                                        // WICHTIG! falls der externe LOW Puls an INT0&lt;br /&gt;
                                        // sehr lange dauert&lt;br /&gt;
&lt;br /&gt;
        PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED für eine Sekunde anschalten&lt;br /&gt;
        long_delay(1000);&lt;br /&gt;
        PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// externer Interrupt INT0 &lt;br /&gt;
&lt;br /&gt;
// Die Interruptroutine kann leer sein, ABER sie muss existieren!&lt;br /&gt;
// Sonst springt der AVR nach dem Aufwachen zum Reset, weil kein sinnvoller&lt;br /&gt;
// Interruptvektor eingetragen ist!&lt;br /&gt;
&lt;br /&gt;
ISR(INT0_vect) {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Power Down Mode beträgt die Stromaufnahme des AVR weniger als 1&amp;amp;mu;A (weniger konnte mit dem verfügbaren Messgerät nicht gemessen werden), laut Datenblatt ca. 0,3&amp;amp;mu;A. Während der einen Sekunde LED Leuchtdauer werden 3,8mA verbraucht. Wird einmal pro Minute die Taste gedrückt, ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {0,3 \mu A \cdot 59s + 3,8mA \cdot 1s}{60s} = 63,6 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird nur einmal pro Stunde die Taste gedrückt, ergibt sich ein mittlerer Stromverbrauch von&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;I_m = \frac {0,3 \mu A \cdot 3559s + 3,8mA \cdot 1s}{3660s} = 1,3 \mu A&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* [http://datasheets.maxim-ic.com/en/ds/DS1371.pdf DS1371], Echtzeituhr mit 1&amp;amp;mu;A Stromverbrauch&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* TODO&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=74xx&amp;diff=25450</id>
		<title>74xx</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=74xx&amp;diff=25450"/>
		<updated>2008-01-09T10:05:53Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Formatierung überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Die 74xx-Serie ist die am weitesten verbreitete [[Digital]]-IC-Familie. Das Präfix gibt Auskunft über den verwendbaren Temperaturbereich.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
! Familie || Temperaturbereich || Einsatzgebiet&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|74||0°C bis +70°C || Standard (engl. Comercial)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|54||-55°C bis +125°C || Militärisch (engl. Military)&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|84||-25°C bis +85°C || Industriell (engl. Industial)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Baureihen ==&lt;br /&gt;
&lt;br /&gt;
* TTL (&#039;&#039;&#039;T&#039;&#039;&#039;ransistor &#039;&#039;&#039;T&#039;&#039;&#039;ransistor &#039;&#039;&#039;L&#039;&#039;&#039;ogic, Logik auf Transistorbasis)&lt;br /&gt;
** 74 = TTL (veraltet)&lt;br /&gt;
** 74H = Highspeed TTL (veraltet)&lt;br /&gt;
** 74ALS = Advanced Low Power Schottky TTL&lt;br /&gt;
** 74AS = Advanced Schottky TTL&lt;br /&gt;
** 74F = Fast TTL&lt;br /&gt;
** 74L = Low Power TTL (veraltet)&lt;br /&gt;
** 74LS = Low Power Schottky TTL (Ersatz für 74 und 74L)&lt;br /&gt;
** 74S = Schottky TTL&lt;br /&gt;
&lt;br /&gt;
* CMOS (&#039;&#039;&#039;C&#039;&#039;&#039;omplementary &#039;&#039;&#039;M&#039;&#039;&#039;etal &#039;&#039;&#039;O&#039;&#039;&#039;xide &#039;&#039;&#039;S&#039;&#039;&#039;emiconductor, Halbleiter mit gegensäzlicher Polarität)&lt;br /&gt;
** 74AC = Advanced CMOS&lt;br /&gt;
** 74ACT = AC mit TTL-kompatiblen Eingängen&lt;br /&gt;
** 74HC = High Speed CMOS&lt;br /&gt;
** 74HCT = HC mit TTL-kompatiblen Eingängen&lt;br /&gt;
** 74AHC =  Advanced High-Speed CMOS&lt;br /&gt;
** 74AHCT = AHC mit TTL-kompatiblen Eingängen&lt;br /&gt;
** 74VHC = Very High Speed CMOS&lt;br /&gt;
** 74VHCT = VHC mit TTL-kompatiblen Eingängen&lt;br /&gt;
** 74LV =  Low-Voltage CMOS&lt;br /&gt;
&lt;br /&gt;
* ECL (&#039;&#039;&#039;E&#039;&#039;&#039;mitter &#039;&#039;&#039;C&#039;&#039;&#039;oupled &#039;&#039;&#039;L&#039;&#039;&#039;ogic, Emittergekoppelte Logik)&lt;br /&gt;
** 74ECL&lt;br /&gt;
** 74ECTL&lt;br /&gt;
&lt;br /&gt;
* Langsame störsichere Logik&lt;br /&gt;
** 74LSL&lt;br /&gt;
** 74SZL&lt;br /&gt;
&lt;br /&gt;
* BICMOS [[Bus]]-Interface-Logik (CMOS und Bipolartechnik kombiniert)&lt;br /&gt;
** 74BCT (siehe [http://focus.ti.com/docs/logic/catalog/overview/overview.jhtml?templateId=5020&amp;amp;path=templatedata/cm/ovw/data/bct_overview Texas Instruments])&lt;br /&gt;
** 74ABT&lt;br /&gt;
&lt;br /&gt;
== Typen ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- übersetzen! --&amp;gt;&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;3&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;background:#ffdead;&amp;quot; | Typ&lt;br /&gt;
! style=&amp;quot;background:#ffdead;&amp;quot; | Pins&lt;br /&gt;
! style=&amp;quot;background:#ffdead;&amp;quot; | Anzahl&lt;br /&gt;
! style=&amp;quot;background:#ffdead;&amp;quot; | Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 00 || 14 || 4 || 2 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 01 || 14 || 4 || 2 Input NAND (OC=[[Ausgangsstufen Logik-ICs#Open_Collector|Open Collector]])&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 02 || 14 || 4 || 2 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 03 || 14 || 4 || 2 Input NAND (OC) Andere Belegung als 7401&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 04 || 14 || 6 || Inverter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 05 || 14 || 6 || Inverter (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 06 || 14 || 6 || Inverter Buffer/Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 07 || 14 || 6 || Buffer/Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 08 || 14 || 4 || 2 Input AND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 09 || 14 || 4 || 2 Input AND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 10 || 14 || 3 || 3 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 11 || 14 || 3 || 3 Input AND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 12 || 14 || 3 || 3 Input NAND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 13 || 14 || 2 || 4 Input NAND [[Schmitt-Trigger]]&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 14 || 14 || 6 || Inverter Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 15 || 14 || 3 || 3 Input AND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 16 || 14 || 6 || Inverter Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 17 || 14 || 6 || Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 18 || 14 || 2 || 4 Input NAND Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 19 || 14 || 6 || Inverter Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 20 || 14 || 2 || 4 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 21 || 14 || 2 || 4 Input AND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 22 || 14 || 2 || 4 Input NAND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 24 || 14 || 4 || 2 Input NAND Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 25 || 14 || 2 || 4 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 26 || 14 || 4 || 2 Input NAND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 27 || 14 || 3 || 3 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 28 || 14 || 4 || 2 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 30 || 14 || 1 || 8 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 31 || 16 || - || Verzögerungs-Element (je 2 Non-Inverting, Inverting, 2 Input NAND)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 32 || 14 || 4 || 2 Input OR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 33 || 14 || 4 || 2 Input NOR (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 34 || 14 || 6 || Treiber&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 35 || 14 || 6 || Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 36 || 14 || 4 || 2 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 37 || 14 || 4 || 2 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 38 || 14 || 4 || 2 Input NAND (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 39 || 14 || 4 || 2 Input NAND Treiber (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 40 || 14 || 2 || 4 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 41 || 16 || - || BCD -&amp;gt; Decimal Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 42 || 16 || - || BCD -&amp;gt; Decimal Decoder&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 43 || 16 || - || Excess-3 -&amp;gt; Decimal Decoder&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 44 || 16 || - || Excess-3-Gray -&amp;gt; Decimal Decoder&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 45 || 16 || - || BCD -&amp;gt; Decimal Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 46 || 16 || - || BCD -&amp;gt; 7-Segment Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 47 || 16 || - || BCD -&amp;gt; 7-Segment Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 48 || 16 || - || BCD -&amp;gt; 7-Segment Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 49 || 14 || - || BCD -&amp;gt; 7-Segment Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 50 || 14 || - || Dual 2-Wide 2-Input AND-OR-INVERT (1 expandable)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 51 || 14 || 2 || AND-OR-INVERT&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 52 || 14 || ? || Expandable AND-OR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 53 || 14 || 1 || Expandable 4-Wide AND-OR-INVERT&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 54 || 14 || 1 || 3-2-2-3 Input AND-OR-INVERT&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 55 || 14 || 1 || 2-Wide 4-Input AND-OR-INVERT&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 73 || 14 || 2 || JK Flip Flop with clear&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 74 || 14 || 2 || D Flip Flop with preset &amp;amp; clear&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 75 || 16 || - || 4-Bit Bistable Latch&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 76 || 14 || 2 || JK Flip Flop with preset &amp;amp; clear&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 78 || 14 || 2 || JK Flip Flop&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 86 || 14 || 4 || 2 Input XOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 90 || 14 || - || Decade Counter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 92 || 14 || - || Divide By-Twelve Couter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 93 || 14 || - || 4-Bit Binary Counter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 95 || 14 || - || 4 Bit Parallel Access Shift Register&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 121 || 14 || - || Monostable Multivibrator With Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 122 || 14 || - || Retriggerable Monostable Multivibrator&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 123 || 16 || 2 || Retriggerable Monostable Multivibrator&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 125 || 14 || 4 || [[Ausgangsstufen Logik-ICs#Tri-state|Tri-State]] Buffer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 126 || 14 || 4 || Tri-State Buffer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 132 || 14 || 4 || 2 Input NAND Schmitt-Trigger&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 133 || 14 || 1 || 13 Input NAND&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 136 || 14 || 4 || 2 Input XOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 137 || 16 || 1 || 3-to-8 line decoder / demultiplexer with address latches, low-active outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 138 || 16 || 1 || 3-to-8 line decoder / demultiplexer , low-active outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 139 || 16 || 2 || 2-to-4 line decoder / demultiplexer , low-active outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 146 || 16 || - || BCD -&amp;gt; Decimal Decoder (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 147 || 16 || - || 10-Line -&amp;gt; 4-Line BCD Priority Encoder&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 148 || 16 || - || 8-Line -&amp;gt; 3-Line Priority Encoder&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 151 || 16 || 1 || 8:1 Multiplexer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 154 || 24 || 1 || 4-Line -&amp;gt; 16-Line Decoder/Demultiplexer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 155 || 16 || 2 || 2-Line -&amp;gt; 4-Line Decoder/Demultiplexer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 156 || 16 || 2 || 2-Line -&amp;gt; 4-Line Decoder/Demultiplexer (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 157 || 16 || 4 || 2:1 Multiplexer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 158 || 16 || 4 || 2:1 Multiplexer , inverted outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 161 || 16 || - || Sync 4 Bit Binary Counter Async Reset&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 164 || 14 || - || 8 Bit Serial Shift Register&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 177 || 14 || - || Presetable Binary Counter/Latch&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 191 || 16 || - || 4-Bit Up/Down Binary Converter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 193 || 16 || - || 4-Bit Up/Down Binary Counter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 221 || 16 || 2 || Monostable Multivibrator with Reset&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 238 || 16 || 1 || 3-to-8 line decoder / demultiplexer , high-active outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 239 || 16 || 2 || 2-to-4 line decoder / demultiplexer , high-active outputs&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 240 || 20 || - || 8-Bit Tri-State Buffer/Line Driver (invertierend)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 241 || 20 || - || 8-Bit Tri-State Buffer/Line Driver&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 242 || 14 || - || 4-Bit Bus Transceiver (invertierend)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 243 || 14 || - || 4-Bit Bus Transceiver (nicht invertierend)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 244 || 20 || - || 8-Bit Tri-State Buffer/Line Driver (nicht invertierend)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 245 || 20 || - || 8-Bit Bus Transceiver&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 251 || 16 || - || 8-Bit Input Multiplexer; 3-State&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 259 || 16 || - || 8-Bit Adressable Latch&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 260 || 14 || 2 || 5 Input NOR&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 266 || 14 || 4 || 2 Input Exclusive NOR (OC)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 366 || 16 || 6 || Tri-State Inverting Buffer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 367 || 16 || 6 || Tri-State Buffer&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 373 || 20 || - || 8-Bit Transparent Latch&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 541 || 20 || - || 8-Bit Tri-State Buffer/Line Driver&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 573 || 20 || - || 8-Bit Tri-State D-Type Latch&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 574 || 20 || 8 || Tri-State Flip Flop&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 590 || 16 || - || 8-Bit binary counter, 3-state output register&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background:#efefef; text-align:right; font-weight: bold;&amp;quot; | 595 || 16 || - || 8-Bit Serial -&amp;gt; Parallel (SIPO) Shift Register&amp;lt;br&amp;gt;siehe auch [[AVR-Tutorial: Schieberegister]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.kingswood-consulting.co.uk/giicm/ Giant Internet IC Masturbator] Umfassende Übersicht mit Pinouts über 4000, 7400 und Co (nach GIICM suchen, falls der Link mal ins Leere zeigen sollte)&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/3725 &amp;quot;Ende der &#039;244 Verwirrung ???&amp;quot;] im Forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25449</id>
		<title>AVR-Tutorial: Schieberegister</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25449"/>
		<updated>2008-01-09T09:50:57Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ab und an stellt sich folgendes Problem: Man würde wesentlich mehr Ausgangspins oder Eingangspins benötigen als der [[Mikrocontroller]] zur Verfügung stellt. Ein möglicher Ausweg ist eine Porterweiterung mit einem Schieberegister. Zwei beliebte Schieberegister sind beispielsweise der 74xx595 bzw. der 74xx165.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Um neue Ausgangspins zu gewinnen kann der [[74xx | 74xx595]] verwendet werden. Dabei handelt es sich um ein &#039;&#039;8-Bit 3-state Serial-in/Serial-out or Parallel-Out Schieberegister mit einem Ausgangsregister und einem asynchronen Reset&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Hinter dieser kompliziert anmutenden Beschreibung verbirgt sich eine einfache Funktionalität: Das Schieberegister besteht aus zwei Funktionseinheiten: Dem eigentlichen Schieberegister und dem Ausgangsregister. In das Schieberegister können die Daten seriell hineingetaktet werden und durch ein bestimmtes Signal werden die Daten des Schieberegisters in das Ausgangsregister übernommen und können von dort auf die Ausgangspins geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Im Einzelnen bedeuten die Begriffe:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Begriff || Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|8-Bit   || Acht Ausgangs[[Bit|bit]]s&lt;br /&gt;
|-&lt;br /&gt;
|3-state || Die acht Registerausgänge können drei Zustände, Low, High und High-Impedanz annehmen.&amp;lt;BR&amp;gt;Siehe [[Ausgangsstufen Logik-ICs]]&lt;br /&gt;
|-&lt;br /&gt;
|Serial-in || Serieller Eingang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Serial-out || Serieller Ausgang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Parallel-Out || Parallele Ausgänge des Ausgangsregisters&lt;br /&gt;
|-&lt;br /&gt;
|Schieberegister || Serielle Daten werden durch den Baustein durchgeschoben&lt;br /&gt;
|-&lt;br /&gt;
|Ausgangsregister || Ein Speicher, welcher die Daten des Schieberegisters zwischenspeichern kann.&amp;lt;BR&amp;gt;Dieses besteht aus acht [[FlipFlop]]s.&lt;br /&gt;
|-&lt;br /&gt;
|Asynchroner Reset || Die Daten im Schieberegister können asynchron zum zurückgesetzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-1.png]]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Die Benennung der Pins in den Datenblättern verschiedener Hersteller unterscheidet sich zum Teil. Die Funktionen der Pins sind jedoch gleich.&lt;br /&gt;
&lt;br /&gt;
{| cellspacing=”0” border=&amp;quot;1&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele von Hersteller-Pinbenennungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! DIL Pin-Nummer || Funktion || Dieses Tutorial || Motorola / ON Semi || Philips         || Fairchild     || SGS|| Texas Instruments&lt;br /&gt;
|-&lt;br /&gt;
|  1 || Ausgang B           || QB || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || QB|| Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  2 || Ausgang C           || QC || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || QC|| Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  3 || Ausgang D           || QD || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || QD|| Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  4 || Ausgang E           || QE || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || QE|| Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  5 || Ausgang F           || QF || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || QF|| Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  6 || Ausgang G           || QG || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;6&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || QG|| Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  7 || Ausgang H           || QH || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || QH|| Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  8 || Masse, 0 V || [nicht dargestellt] || GND            || GND             || GND             || GND|| GND&lt;br /&gt;
|-&lt;br /&gt;
|  9 || Serieller Ausgang || QH* || SQ&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;´  ||Q&#039;&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  ||  QH´||Q&amp;lt;sub&amp;gt;H&#039;&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 10 || Reset für Schieberegister             || SCL || RESET          || /MR             || /SCLR        || /SCLR || /SRCLR&lt;br /&gt;
|-&lt;br /&gt;
| 11 || Schiebetakt        || SCK || SHIFT CLOCK    || SH&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || SCK          || SCK || SRCLK&lt;br /&gt;
|-&lt;br /&gt;
| 12 || Speichertakt        || RCK || LATCH CLOCK    || ST&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || RCK          || RCK || RCLK&lt;br /&gt;
|-&lt;br /&gt;
| 13 || Ausgangssteuerung      || G || OUTPUT ENABLE  || /OE             || /G           || /G || /OE&lt;br /&gt;
|-&lt;br /&gt;
| 14 || Serieller Dateneingang  || SER || A              || D&amp;lt;sub&amp;gt;S&amp;lt;/sub&amp;gt;   || SER          || SI || SER&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Ausgang A           || QA || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || QA|| Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 16 || Betriebsspannung || [nicht dargestellt] || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;|| V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Baustein besteht aus zwei Einheiten:&lt;br /&gt;
* dem Schieberegister&lt;br /&gt;
* dem Ausgangsregister&lt;br /&gt;
&lt;br /&gt;
Im Schieberegister werden die einzelnen Bits durchgeschoben. Mit jeder positiven Taktflanke(LOW -&amp;gt; HIGH) an &#039;&#039;&#039;SCK&#039;&#039;&#039; wird eine Schiebeoperation durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Das Ausgangsregister hat die Aufgabe die Ausgangspins des Bausteins anzusteuern. Durch dieses Ausgangsregister ist es möglich, die Schiebeoperationen im Hintergrund durchzuführen, ohne dass IC Pins ihren Wert ändern. Erst wenn die Schiebeoperation abgeschlossen ist, wird der aktuelle Zustand der Schieberegisterkette durch einen Puls an &#039;&#039;&#039;RCK&#039;&#039;&#039; in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
===Funktionsweise===&lt;br /&gt;
&lt;br /&gt;
Am Eingang &#039;&#039;&#039;SER&#039;&#039;&#039; (Pin 14) wird das gewünschte nächste Datum (0 oder 1) angelegt. Durch einen positiven Puls an &#039;&#039;&#039;SCK&#039;&#039;&#039; (Pin 1) wird der momentan an &#039;&#039;&#039;SER&#039;&#039;&#039; anliegende Wert als neuer Wert für Bit 0, das unterste Bit des Schieberegisters, übernommen. Gleichzeitig werden alle anderen Bits im Schieberegister um eine Stelle verschoben: Das Bit 6 wird ins Bit 7 übernommen, Bit 5 ins Bit 6, Bit 4 ins Bit 5, etc. sodass das Bit 0 zur Aufnahme des &#039;&#039;&#039;SER&#039;&#039;&#039; Bits frei wird.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-2.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Sonderstellung nimmt das ursprüngliche Bit 7 ein. Dieses Bit steht direkt auch am Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; (Pin 9) zur Verfügung. Dadurch ist es möglich an ein Schieberegister einen weiteren Baustein 74xxx595 anzuschliessen und so beliebig viele Schieberegister hintereinander zu schalten (kaskadieren). Auf diese Art lassen sich Schieberegister mit beliebig vielen Stufen aufbauen.&lt;br /&gt;
&lt;br /&gt;
Wurde das Schieberegister mit den Daten gefüllt, so wird mit einem LOW-HIGH Puls am Pin 12, &#039;&#039;&#039;RCK&#039;&#039;&#039; der Inhalt des Schieberegisters in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-3.png]]&lt;br /&gt;
&lt;br /&gt;
Mit dem Eingang &#039;&#039;&#039;G&#039;&#039;&#039; (Pin 13) kann das Ausgangsregister freigegeben werden. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 0, so führen die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; entsprechende Pegel. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 1, so schalten die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; auf [[Ausgangsstufen Logik-ICs |Tristate]]. D.h. sie treiben aktiv weder LOW oder HIGH, sondern sind hochohmig wie ein Eingänge und nehmen jeden Pegel an, der ihnen von aussen aufgezwungen wird.&lt;br /&gt;
&lt;br /&gt;
Bleibt nur noch der Eingang &#039;&#039;&#039;SCL&#039;&#039;&#039;(Pin 13). Mit ihm kann das Schieberegister im Baustein gelöscht, also auf eine definierte 0, gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Die Programmierung eines 74xxx595 Schieberegisters gestaltet sich sehr einfach. Im Grunde gibt es 2 Möglichkeiten:&lt;br /&gt;
* Mittels [[SPI]] kann der [[AVR]] das Schieberegister direkt und autark ansteuern. Das ist sehr schnell und verbraucht nur wenig CPU-Leistung&lt;br /&gt;
* Sind die entsprechenden SPI-Pins am AVR nicht frei, so ist auch eine softwaremässige Ansteuerung des Schieberegisters mit einfachen Mitteln durchführbar.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software===&lt;br /&gt;
&lt;br /&gt;
Für eine komplette Softwarelösung kann das Schieberegister an jede beliebige Port-Pin Kombination angeschlossen werden. Wir wählen die Pins &#039;&#039;&#039;PB0&#039;&#039;&#039;, &#039;&#039;&#039;PB1&#039;&#039;&#039;, &#039;&#039;&#039;PB2&#039;&#039;&#039; und &#039;&#039;&#039;PB3&#039;&#039;&#039; um dort die Schieberegisteranschlüsse &#039;&#039;&#039;SER&#039;&#039;&#039;, &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;SCL&#039;&#039;&#039; und &#039;&#039;&#039;RCK&#039;&#039;&#039; anzuschliessen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Programmierung gestaltet sich dann nach folgendem Schema: Die 8 Bits eines Bytes werden nacheinander an den Ausgang &#039;&#039;&#039;PB0&#039;&#039;&#039; (&#039;&#039;&#039;SER&#039;&#039;&#039;) ausgegeben. Durch Generierung eines Pulses 0-1-0 an Pin &#039;&#039;&#039;PB1&#039;&#039;&#039; (&#039;&#039;&#039;SCK&#039;&#039;&#039;) übernimmt das Schieberegister nacheinander die einzelnen Bits. Dabei ist zu beachten, dass die Ausgabe mit dem höherwertigen Bit beginnen muss, denn dieses Bit wandert ja am weitesten zur Stelle &#039;&#039;&#039;QH&#039;&#039;&#039;. Sind alle 8 Bits ausgegeben, so wird durch einen weiteren 0-1-0 Impuls am Pin &#039;&#039;&#039;PB3&#039;&#039;&#039; (&#039;&#039;&#039;RCK&#039;&#039;&#039;) der Inhalt der Schieberegisterbits 0 bis 7 in die Ausgaberegister &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; übernommen. Dadurch, dass am Schieberegister der Eingang &#039;&#039;&#039;G&#039;&#039;&#039; konstant auf 0-Pegel gehalten wird, erscheint dann auch die Ausgabe sofort an den entsprechenden Pins und kann zb. mit LEDs (low-current LEDs + Vorwiderstand verwenden) sichtbar gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Der Schieberegistereingang &#039;&#039;&#039;SCL&#039;&#039;&#039; wird auf einer 1 gehalten. Würde er&lt;br /&gt;
auf 0 gehen, so würde die Schieberegisterkette gelöscht. Möchte man einen weiteren Prozessorpin einsparen, so kann man diesen Pin auch generell auf Vcc legen. Das Schieberegister könnte man in so einem Fall durch Einschreiben von 0x00 immer noch löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = 3&lt;br /&gt;
.equ SCK          = 1&lt;br /&gt;
.equ SCL          = 2&lt;br /&gt;
.equ SIN          = 0&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Die Port Pins auf Ausgang konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, (1&amp;lt;&amp;lt;RCK) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;SCL) | (1&amp;lt;&amp;lt;SIN)&lt;br /&gt;
    out   SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; die Clear Leitung am Schieberegister auf 1 stellen&lt;br /&gt;
;&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCL&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe&lt;br /&gt;
    rcall SchiebeOut&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Ausgabe im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
Schiebe:&lt;br /&gt;
    push  temp2&lt;br /&gt;
    ldi   temp2, 8             ; 8 Bits müssen ausgegeben werden&lt;br /&gt;
&lt;br /&gt;
Schiebe_1:&lt;br /&gt;
     ;&lt;br /&gt;
     ; jeweils das höchstwertige Bit aus temp1 ins Carry-Flag schieben&lt;br /&gt;
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend&lt;br /&gt;
     ; gesetzt oder gelöscht&lt;br /&gt;
     ;&lt;br /&gt;
    rol  temp1                 ; MSB -&amp;gt; Carry&lt;br /&gt;
    brcs Schiebe_One           ; Carry gesetzt? -&amp;gt; weiter bei Schiebe_One&lt;br /&gt;
    cbi  SCHIEBE_PORT, SIN     ; Eine 0 ausgeben&lt;br /&gt;
    rjmp Schiebe_Clock         ; und Sprung zur Clock Puls Generierung&lt;br /&gt;
Schiebe_One:&lt;br /&gt;
    sbi  SCHIEBE_PORT, SIN     ; Eine 1 ausgeben&lt;br /&gt;
&lt;br /&gt;
     ;&lt;br /&gt;
     ; einen Impuls an SCK zur Übernahme des Bits nachschieben&lt;br /&gt;
     ;&lt;br /&gt;
Schiebe_Clock:&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCK    ; Clock-Ausgang auf 1 ...&lt;br /&gt;
    cbi   SCHIEBE_PORT, SCK    ; und wieder zurück auf 0&lt;br /&gt;
&lt;br /&gt;
    dec   temp2                ; Anzahl der ausgegebenen Bits runterzählen&lt;br /&gt;
    brne  Schiebe_1            ; Wenn noch keine 8 Bits ausgegeben -&amp;gt; Schleife bilden&lt;br /&gt;
&lt;br /&gt;
    pop   temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul===&lt;br /&gt;
&lt;br /&gt;
Noch schneller geht die Ansteuerung des Schieberegisters mittels [[Serial_Peripheral_Interface | SPI]]-Modul, welches in fast allen AVRs vorhanden ist. Hier wird der Pin &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt, da das praktisch keinen Sinn hat. Er muss also fest auf VCC gelegt werden. Die Pins für &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;SIN&#039;&#039;&#039; sind duch den jeweiligen AVR fest vorgegeben. &#039;&#039;&#039;SCK&#039;&#039;&#039; vom 74xxx595 wird mit &#039;&#039;&#039;SCK&#039;&#039;&#039; vom AVR verbunden sowie &#039;&#039;&#039;SIN&#039;&#039;&#039; mit &#039;&#039;&#039;MOSI&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;O&#039;&#039;&#039;ut, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;I&#039;&#039;&#039;n). &#039;&#039;&#039;MISO&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;I&#039;&#039;&#039;n, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;O&#039;&#039;&#039;ut) ist hier ungenutzt. Es kann NICHT als &#039;&#039;&#039;RCK&#039;&#039;&#039; verwendet werden, da es im SPI-Master Modus immer ein Eingang ist! Es kann aber als allgemeiner Eingang verwendet werden. Der AVR-Pin &#039;&#039;&#039;SS&#039;&#039;&#039; wird sinnvollerweise als &#039;&#039;&#039;RCK&#039;&#039;&#039; benutzt, da er sowieso als Ausgang geschaltet werden &#039;&#039;&#039;muss&#039;&#039;&#039;, sonst gibt es böse Überaschungen (siehe Datenblatt &amp;quot;SS Pin Functionality&amp;quot;). Je nach Bedarf kann man die Taktrate des SPI-Moduls zwischen 1/2 ... 1/128 des CPU-Taktes wählen. Es spricht kaum etwas dagegen mit maximaler Geschwindigkeit zu arbeiten. Die AVRs können zur Zeit mit maximal 20 MHz getaktet werden, d.h. es sind maximal 10 MHz SPI-Takt möglich. Das ist für ein 74xxx595 kein Problem. Die Übertragung von 8 Bit dauert dann gerade mal 800ns!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in    temp1,SCHIEBE_DDR&lt;br /&gt;
    ori   temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out   SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b01010000&lt;br /&gt;
    out   SPCR, temp1           ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   temp1,1&lt;br /&gt;
    out   SPSR,temp1            ; double speed aktivieren&lt;br /&gt;
    out   SPDR,temp1            ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe               ; Daten schieben&lt;br /&gt;
    rcall SchiebeOut            ; Daten in Ausgangsregister übernehmen&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Daten im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
;&lt;br /&gt;
Schiebe:&lt;br /&gt;
    sbis    SPSR,7          ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe&lt;br /&gt;
    out     SPDR,temp1      ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Kaskadieren von Schieberegistern===&lt;br /&gt;
&lt;br /&gt;
Um ein Schieberegister anzuschließen genügen also im einfachsten Fall 4 freie Prozessorpins (3 wenn &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt wird) um weitere 8 Ausgangsleitungen zu bekommen. Genügen diese 8 Leitungen nicht, so kann ohne Probleme ein weiteres Schieberegister an das bereits Vorhandene angeschlossen werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595-2.gif]]&lt;br /&gt;
&lt;br /&gt;
Das nächste Schieberegister wird mit seinem Dateneingang &#039;&#039;&#039;SER&#039;&#039;&#039; einfach an den dafür vorgesehenen Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; des vorhergehenden Schieberegisters angeschlossen. Die Steuerleitungen &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;RCK&#039;&#039;&#039; und &#039;&#039;&#039;SCL&#039;&#039;&#039; werden parallel zu den bereits vorhandenen geschaltet. Konzeptionell erhält man dadurch ein Schieberegister mit einer Breite von 16 Bit. Werden weiter Bausteine in derselben Manier angeschlossen, so erhöht sich die Anzahl der zur Verfügung stehenden Ausgabeleitungen mit jedem Baustein um 8 ohne dass sich die Anzahl der am Prozessor notwendigen Ausgabepins erhöhen würde. Um diese weiteren Register zu nutzen, muss man in der reinen Softwarelösung nur mehrfach die Funktion &#039;&#039;&#039;Schiebe&#039;&#039;&#039; aufrufen, um alle Daten auszugeben. Am Ende werden dann mit &#039;&#039;&#039;SchiebeOut&#039;&#039;&#039; die Daten in die Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
Bei der SPI Lösung werden ebenfalls ganz einfach mehrere Bytes über SPI ausgegeben, ehe dann mittels &#039;&#039;&#039;RCK&#039;&#039;&#039; die in die Schieberegisterkette eingetakteten Bits in das Ausgangsregister übernommen werden.&lt;br /&gt;
Um das Ganze ein wenig zu vereinfachen, soll hier eine Funktion zur Ansteuerung mehrerer kaskadierter Schieberegister über das SPI-Modul gezeigt werden. Dabei wird die Ausgabe mehrerer Bytes über eine Schleife realisiert, mehrfache Aufrufe der Funktion sind damit nicht nötig. Statt dessen übergibt man einen Zeiger auf einen Datenblock im RAM sowie die Anzahl der zu übertragenden Bytes. Ausserdem wird die Datenübernahme durch &#039;&#039;&#039;RCK&#039;&#039;&#039; standardkonform integriert. Denn bei nahezu allen ICs mit SPI wird ein sog. CS-Pin verwendet (&#039;&#039;&#039;C&#039;&#039;&#039;hip &#039;&#039;&#039;S&#039;&#039;&#039;elect) Dieser Pin ist meist LOW aktiv, d.h. wenn er HIGH ist, ignoriert der IC alle Signale an &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;MOSI&#039;&#039;&#039; und gibt keine Daten an MISO aus. Ist er LOW, dann ist der IC aktiv und funktioniert normal. Bei der steigenden Flanke an &#039;&#039;&#039;CS&#039;&#039;&#039; werden die Daten ins Ausgangsregister übernommen. Die Fuktion ist sehr schnell, da die Zeit während der die Übertragung eines Bytes läuft, dazu genutzt wird, den Schleifenzähler zu verringern und zu prüfen sowie neue Sendedaten zu laden. Zwischen den einzelnen Bytes gibt es somit nur eine Pause von max. 6 Systemtakten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org $60&lt;br /&gt;
Schiebedaten:       .byte 2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in      temp1,SCHIEBE_DDR&lt;br /&gt;
    ori     temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out     SCHIEBE_DDR,temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; Slave select inaktiv&lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi     temp1, 0b01010000&lt;br /&gt;
    out     SPCR, temp1         ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi     r16,1&lt;br /&gt;
    out     SPSR,r16            ; Double Speed&lt;br /&gt;
    out     SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; den Datenblock mit Daten füllen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1,$F0&lt;br /&gt;
    sts     Schiebedaten,temp1&lt;br /&gt;
    ldi     temp1,$55&lt;br /&gt;
    sts     Schiebedaten+1,temp1&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
; den Datenblock ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     r16,2&lt;br /&gt;
    ldi     zl,low(Schiebedaten)&lt;br /&gt;
    ldi     zh, high(Schiebedaten)&lt;br /&gt;
    rcall   Schiebe_alle                    ; Daten ausgeben&lt;br /&gt;
&lt;br /&gt;
    rjmp  loop                              ; nur zur Simulation&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes an das Schieberegister ausgeben und in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; r16: Anzahl der Datenbytes&lt;br /&gt;
; Z: Zeiger auf Datenblock im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
Schiebe_alle:&lt;br /&gt;
    cbi     SCHIEBE_PORT, RCK   ; RCK LOW, SPI Standardverfahren&lt;br /&gt;
    push    r17&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_2:&lt;br /&gt;
    ld      r17,Z+&lt;br /&gt;
Schiebe_alle_3:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_3&lt;br /&gt;
    out     SPDR,r17            ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    dec     r16&lt;br /&gt;
    brne    Schiebe_alle_2&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_4:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob die letzte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_4&lt;br /&gt;
&lt;br /&gt;
    pop     r17&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; RCK inaktiv, Datenübernahme&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Nachteil von Schieberegistern ist allerdings, dass sich die Zeit zum Setzten aller Ausgabeleitungen mit jedem weiteren Baustein immer weiter erhöht. Dies deshalb, da ja die einzelnen Bits im Gänsemarsch durch alle Bausteine geschleust werden müssen und für jeden einzelnen Schiebevorgang etwas Zeit notwendig ist. Ein Ausweg ist die Verwendung des SPI-Moduls, welches schneller arbeitet als die reine Softwarelösung. Ist noch mehr Geschwindigkeit gefragt, so sind mehr Port-Pins nötig. Kann ein kompletter Port mit 8 Pins für die Daten genutzt werden, sowie ein paar weitere Steuerleitungen, so können ein oder mehrere 74xxx573 eine Alternative sein, um jeweils ein vollständiges Byte auszugeben. Natürlich kann der 74xxx573 (oder ein ähnliches Schieberegister) auch mit dem 74xxx595 zusammen eingesetzt werden, beispielsweise in dem über das Schieberegister verschiedene 74xxx595 nacheinander aktiviert werden. Weitere Tips und Tricks dazu gibt es vielleicht in einem weiteren Tutorial...&lt;br /&gt;
&lt;br /&gt;
=== Acht LEDs mit je 20mA pro Schieberegister ===&lt;br /&gt;
&lt;br /&gt;
Will man nun acht [[LED]]s mit dem Schieberegister ansteuern, kann man diese direkt über Vorwiderstände anschliessen. Doch ein genauer Blick ins Datenblatt verrät, dass der 74xx595 nur maximal 70mA über VCC bzw. GND ableiten kann. Und wenn man den IC nicht gnadenlos quälen, und damit die Lebensdauer und Zuverlässigkeit drastisch reduzieren will, gibt es nur zwei Auswege.&lt;br /&gt;
&lt;br /&gt;
* Den Strom pro LED auf 70/8 = 8,75mA begrenzen; Das reicht meistens aus um die LEDs schön leuchten zu lassen, vor allem bei low-current und ultrahellen LEDs&lt;br /&gt;
* Wenn doch 20 mA pro LED gebraucht werden, kann man die folgende Trickschaltung anwenden.&lt;br /&gt;
&lt;br /&gt;
[[bild:8x20mA_LED_mit_74xx595.png]]&lt;br /&gt;
&lt;br /&gt;
Der Trick besteht darin, dass 4 LEDs ihren Strom über das Schieberegister von VCC beziehen (HIGH aktiv) während die anderen vier ihren Strom über GND leiten (LOW aktiv). Damit bleiben ganz offiziell für jede LED 70/4 = 17,5mA. Um die Handhabung in der Software zu vereinfachen muss nur vor der Ausgabe der Daten das jeweilige Byte mit 0x0F XOR verknüpft werden, bevor es in das Schieberegister getaktet wird. Dadurch werden die LOW-aktiven LEDs richtig angesteuert und die Datenhandhabung in der Software muss nur mit HIGH-aktiven rechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Achtung!&amp;lt;/B&amp;gt; Die Widerstände sind auf blaue LEDs mit 3,3V Flusspannung ausgelegt. Bei roten, gelben und grünen [[LED]]s ist die Flusspannung geringer und dementsprechend muss der Vorwiderstand grösser sein.&lt;br /&gt;
&lt;br /&gt;
Ausserdem wird der G Eingang verwendet, um die Helligkeit aller LEDs per [[PWM]] zu steuern. Beachtet werden muss, dass die PWM im invertierten Modus generiert werden muss, da der Eingang G LOW aktiv ist.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Eingänge ==&lt;br /&gt;
&lt;br /&gt;
Ein naher Verwandter des 75xx595 ist der [[74xx | 74xx165]], er ist quasi das Gegenstück. Hierbei handet es sich um ein &#039;&#039;8-bit parallel-in/serial-out shift register&#039;&#039;. Auf deutsch ein 8 Bit Schieberegister mit parallelem Eingang und seriellem Ausgang. Damit kann man ein grosse Anzahl Eingänge sehr einfach und preiswert zu seinem Mikrocontroller hinzufügen.&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-1.png]]&lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist sehr ähnlich zum 74xx595. Allerdings gibt es kein Register zum Zwischenspeichern. Das ist auch gar nicht nötig, da der IC ja einen parallelen Eingang hat. Der muss nicht zwischengespeichert werden. Es gibt hier also wirklich nur das Schieberegister. Dieses wird über den Eingang PL mit den parallelen Daten geladen. Dann können die Daten seriell mit Takten an CLK aus dem Ausgang Q7 geschoben werden.&lt;br /&gt;
 &lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
DS ist der serielle Dateneingang, welcher im Falle von kaskadierten Schieberegistern mit dem Ausgang des vorhergehenden ICs verbunden wird.&lt;br /&gt;
&lt;br /&gt;
D0..D7 sind die parallelen Dateneingänge.&lt;br /&gt;
&lt;br /&gt;
Mittels des Eingangs PL (&#039;&#039;&#039;P&#039;&#039;&#039;arallel &#039;&#039;&#039;L&#039;&#039;&#039;oad) werden die Daten vom parallelen Eingang in das Schieberegister übernommen, wenn dieses Signal LOW ist. Hier muss man aber ein klein wenig aufpassen. Auf grund der Schaltungsstruktur ist der Eingang PL mit dem Takt CLK verknüpft (obwohl es dafür keinen logischen Grund gibt :-0). Damit es nicht zu unerwünschten Fehlschaltungen kommt, muss der Takt CLK während des Ladens auf HIGH liegen. Wird PL wieder auf HIGH gesetzt, sind die Daten geladen. Das erste Bit liegt direkt am Ausgang Q7 and. Die restlichen Bits können nach und nach durch das Register geschoben werden.&lt;br /&gt;
&lt;br /&gt;
Der Eingang CE (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;E&#039;&#039;&#039;nable) steuert, ob das Schieberegister auf den Takt CLK reagieren soll oder nicht. Ist CE gleich LOW werden alle Takte an CLK ignoriert. Bei HIGH werden mit jeder positiven Flanke die Daten um eine Stufe weiter geschoben.&lt;br /&gt;
&lt;br /&gt;
Wird am Eingang CLK eine LOW-HIGH Flanke angelegt und ist dabei CE auf LOW, dann werden die Daten im Schieberegister um eine Position weiter geschoben: DS-&amp;gt;Q0, Q0-&amp;gt;Q1, Q1-&amp;gt;Q2, Q2-&amp;gt;Q3, Q3-&amp;gt;Q4, Q4-&amp;gt;Q5, Q5-&amp;gt;Q6, Q6-&amp;gt;Q7. Q0..Q6 sind interne Signale, siehe [http://www.nxp.com/acrobat/datasheets/74HC_HCT165_CNV_2.pdf Datenblatt].&lt;br /&gt;
&lt;br /&gt;
Q7 ist der Ausgang des Schieberegisters. Dort Werden Takt für Takt die Daten ausgeggeben. Hier wird normalerweise der Eingang des Mikrocontrollers oder der Eingang des nächsten Schieberegisters angeschlossen.&lt;br /&gt;
&lt;br /&gt;
Q7\ ist der invertierte Ausgang des Schieberegisters. Er wird meist nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Schaltung ===&lt;br /&gt;
&lt;br /&gt;
Um nun beispielsweise zwei Schieberegister zu kaskadieren um 16 Eingangspins zu erhalten sollte man folgende Verschaltung vornehmen. Beachten sollte man dabei, dass&lt;br /&gt;
&lt;br /&gt;
* der serielle Eingang DS des ersten Schieberegisters (hier IC1) auf einen festen Pegel gelegt wird (LOW oder HIGH).&lt;br /&gt;
* der serielle Datenausgang bei der Benutzung des SPI-Moduls an MISO und nicht an MOSI angeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-2.png]]&lt;br /&gt;
&lt;br /&gt;
Nachfolgend werden zwei Beispiele gezeigt, welche die Ansteuerung nach bekanntem Muster übernehemen. Nur dass hier eben Daten gelesen anstatt geschrieben werden. Zu beachten ist, dass hier ein anderer Modus der SPI-Ansteuerung verwendet werden muss, weil der Baustein das nötig macht. Das muss beachtet werden, wenn auch Schieberegister für Ausgänge verwendet werden. Dabei muss jeweils vor dem Zugriff auf die Ein- oder Ausgangsregister der Modus des Taktes (CPOL) umgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per Software&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen, frei wählbar&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ SCHIEBE_PIN  = PINB&lt;br /&gt;
.equ CLK          = PB3&lt;br /&gt;
.equ PL           = PB1&lt;br /&gt;
.equ DIN          = PB2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:      .byte 2             ; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;clk) | (1&amp;lt;&amp;lt;pl)&lt;br /&gt;
    out     SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     schiebe_port, clk   ; Takt im Ruhezustand immer auf 1&lt;br /&gt;
                                ; komische Schaltung im 74xx165&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL,low(Daten)&lt;br /&gt;
    ldi     ZH,high(Daten)&lt;br /&gt;
    ldi     temp1,2&lt;br /&gt;
    rcall   schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push    temp2               ; Register sichern&lt;br /&gt;
    push    temp3&lt;br /&gt;
&lt;br /&gt;
    cbi     schiebe_port, pl    ; Daten parallel laden&lt;br /&gt;
    sbi     schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
&lt;br /&gt;
    ldi     temp3, 8            ; Bitzähler&lt;br /&gt;
schiebe_eingang_bit_schleife:&lt;br /&gt;
    lsl     temp2               ; Daten weiterschieben&lt;br /&gt;
&lt;br /&gt;
; das IO Bit Din in das niederwerigste Bit von temp2 kopieren&lt;br /&gt;
&lt;br /&gt;
    sbic    schiebe_pin, din    ; wenn Null, nächsten Befehl überspringen&lt;br /&gt;
    ori     temp2,1             ; nein, Bit setzen&lt;br /&gt;
&lt;br /&gt;
    cbi     SCHIEBE_PORT, CLK   ; Taktausgang auf 0&lt;br /&gt;
    sbi     SCHIEBE_PORT, CLK   ; und wieder zurück auf 1, dabei Daten schieben &lt;br /&gt;
&lt;br /&gt;
    dec     temp3               ; Bitzähler um eins verringern&lt;br /&gt;
    brne    schiebe_eingang_bit_schleife ;wenn noch keine 8 Bits ausgegeben, nochmal&lt;br /&gt;
&lt;br /&gt;
    st      z+,temp2            ; Datenbyte speichern&lt;br /&gt;
    dec     temp1               ; Anzahl Bytes um eins verringern&lt;br /&gt;
    brne    schiebe_eingang_byte_schleife   ; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
    pop     temp3&lt;br /&gt;
    pop     temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per SPI-Modul&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen&lt;br /&gt;
; diese müssen mit den SPI-Pins des AVR Typs übereinstimmen!&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ PL           = PB2         ; SS&lt;br /&gt;
.equ CLK          = PB5         ; SCK&lt;br /&gt;
.equ DIN          = PB4         ; MISO&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:      .byte 2             ; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1,(1&amp;lt;&amp;lt;CLK) | (1&amp;lt;&amp;lt;PL)&lt;br /&gt;
    out     SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi     temp1, 0b01011000&lt;br /&gt;
    out     SPCR, temp1         ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 1, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi     temp1,1&lt;br /&gt;
    out     SPSR,temp1          ; double speed aktivieren&lt;br /&gt;
    out     SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL,low(Daten)&lt;br /&gt;
    ldi     ZH,high(Daten)&lt;br /&gt;
    ldi     temp1,2&lt;br /&gt;
    rcall   schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push    temp2               ; Register sichern&lt;br /&gt;
&lt;br /&gt;
    ; CLK ist im Ruhezustand schon auf HIGH, CPOL=1&lt;br /&gt;
&lt;br /&gt;
    cbi     schiebe_port, pl    ; Daten parallel laden&lt;br /&gt;
    sbi     schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_1:&lt;br /&gt;
    sbis    SPSR,7              ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    schiebe_eingang_1&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
    out     SPDR,temp1          ; beliebige Daten ins SPI Modul schreiben&lt;br /&gt;
                                ; um die Übertragung zu starten&lt;br /&gt;
schiebe_eingang_2:&lt;br /&gt;
    sbis    SPSR,7              ; auf das Ende der Übertragung warten&lt;br /&gt;
    rjmp    schiebe_eingang_2&lt;br /&gt;
&lt;br /&gt;
    in      temp2, spdr         ; Daten lesen&lt;br /&gt;
    st      z+,temp2            ; Datenbyte speichern&lt;br /&gt;
    dec     temp1               ; Anzahl Bytes um eins verringern&lt;br /&gt;
    brne    schiebe_eingang_byte_schleife   ; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
    pop     temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
AVR Studio 4.12 (Build 498) hat Probleme bei der korrekten Simulation des SPI-Moduls.&lt;br /&gt;
&lt;br /&gt;
* Der Double-Speed Modus funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
* Das Bit SPIF im Register SPSR, welches laut Dokumentation nur lesbar ist, ist im Simulator auch schreibbar! Das kann zu Verwirrung und Fehlern in der Simulation führen.&lt;br /&gt;
&lt;br /&gt;
Hardwareprobleme&lt;br /&gt;
&lt;br /&gt;
* Wenn das SPI-Modul aktiviert wird, wird &#039;&#039;&#039;NICHT&#039;&#039;&#039; automatisch SPIF gesetzt, es bleibt auf Null. Damit würde die erste Abfrage in &#039;&#039;Schiebe_alles&#039;&#039; in einer Endlosschleife hängen bleiben. Deshalb muss nach der Initialisierung des SPI-Moduls ein Dummy Byte gesendet werden, damit am Ende der Übertragung SPIF gesetzt wird&lt;br /&gt;
&lt;br /&gt;
* Da das SPI-Modul in Senderichtung nur einfach gepuffert ist, ist es nicht möglich absolut lückenlos Daten zu senden, auch wenn man mit &#039;&#039;&#039;nop&#039;&#039;&#039; eine feste minimale Zeit zwischen zwei Bytes warten würde. Zwischen zwei Bytes muss immer eine Pause von mind. 2 Systemtakten eingehalten werden.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=PWM|&lt;br /&gt;
zurücklink=AVR-Tutorial: PWM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=SRAM|&lt;br /&gt;
vorlink=AVR-Tutorial: SRAM}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25438</id>
		<title>AVR-Tutorial: Schieberegister</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25438"/>
		<updated>2008-01-08T22:45:16Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Funktionsweise */ Link eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ab und an stellt sich folgendes Problem: Man würde wesentlich mehr Ausgangspins oder Eingangspins benötigen als der [[Mikrocontroller]] zur Verfügung stellt. Ein möglicher Ausweg ist eine Porterweiterung mit einem Schieberegister. Zwei beliebte Schieberegister sind beispielsweise der 74xx595 bzw. der 74xx165.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Um neue Ausgangspins zu gewinnen kann der 74xx595 verwendet werden. Dabei handelt es sich um ein &#039;&#039;8-Bit 3-state Serial-in/Serial-out or Parallel-Out Schieberegister mit einem Ausgangsregister und einem asynchronen Reset&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Hinter dieser kompliziert anmutenden Beschreibung verbirgt sich eine einfache Funktionalität: Das Schieberegister besteht aus zwei Funktionseinheiten: Dem eigentlichen Schieberegister und dem Ausgangsregister. In das Schieberegister können die Daten seriell hineingetaktet werden und durch ein bestimmtes Signal werden die Daten des Schieberegisters in das Ausgangsregister übernommen und können von dort auf die Ausgangspins geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Im Einzelnen bedeuten die Begriffe:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Begriff || Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|8-Bit   || Acht Ausgangs[[Bit|bit]]s&lt;br /&gt;
|-&lt;br /&gt;
|3-state || Die acht Registerausgänge können drei Zustände, Low, High und High-Impedanz annehmen.&amp;lt;BR&amp;gt;Siehe [[Ausgangsstufen Logik-ICs]]&lt;br /&gt;
|-&lt;br /&gt;
|Serial-in || Serieller Eingang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Serial-out || Serieller Ausgang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Parallel-Out || Parallele Ausgänge des Ausgangsregisters&lt;br /&gt;
|-&lt;br /&gt;
|Schieberegister || Serielle Daten werden durch den Baustein durchgeschoben&lt;br /&gt;
|-&lt;br /&gt;
|Ausgangsregister || Ein Speicher, welcher die Daten des Schieberegisters zwischenspeichern kann.&amp;lt;BR&amp;gt;Dieses besteht aus acht [[FlipFlop]]s.&lt;br /&gt;
|-&lt;br /&gt;
|Asynchroner Reset || Die Daten im Schieberegister können asynchron zum zurückgesetzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-1.png]]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Die Benennung der Pins in den Datenblättern verschiedener Hersteller unterscheidet sich zum Teil. Die Funktionen der Pins sind jedoch gleich.&lt;br /&gt;
&lt;br /&gt;
{| cellspacing=”0” border=&amp;quot;1&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele von Hersteller-Pinbenennungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! DIL Pin-Nummer || Funktion || Dieses Tutorial || Motorola / ON Semi || Philips         || Fairchild     || SGS|| Texas Instruments&lt;br /&gt;
|-&lt;br /&gt;
|  1 || Ausgang B           || QB || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || QB|| Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  2 || Ausgang C           || QC || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || QC|| Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  3 || Ausgang D           || QD || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || QD|| Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  4 || Ausgang E           || QE || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || QE|| Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  5 || Ausgang F           || QF || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || QF|| Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  6 || Ausgang G           || QG || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;6&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || QG|| Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  7 || Ausgang H           || QH || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || QH|| Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  8 || Masse, 0 V || [nicht dargestellt] || GND            || GND             || GND             || GND|| GND&lt;br /&gt;
|-&lt;br /&gt;
|  9 || Serieller Ausgang || QH* || SQ&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;´  ||Q&#039;&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  ||  QH´||Q&amp;lt;sub&amp;gt;H&#039;&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 10 || Reset für Schieberegister             || SCL || RESET          || /MR             || /SCLR        || /SCLR || /SRCLR&lt;br /&gt;
|-&lt;br /&gt;
| 11 || Schiebetakt        || SCK || SHIFT CLOCK    || SH&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || SCK          || SCK || SRCLK&lt;br /&gt;
|-&lt;br /&gt;
| 12 || Speichertakt        || RCK || LATCH CLOCK    || ST&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || RCK          || RCK || RCLK&lt;br /&gt;
|-&lt;br /&gt;
| 13 || Ausgangssteuerung      || G || OUTPUT ENABLE  || /OE             || /G           || /G || /OE&lt;br /&gt;
|-&lt;br /&gt;
| 14 || Serieller Dateneingang  || SER || A              || D&amp;lt;sub&amp;gt;S&amp;lt;/sub&amp;gt;   || SER          || SI || SER&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Ausgang A           || QA || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || QA|| Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 16 || Betriebsspannung || [nicht dargestellt] || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;|| V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Baustein besteht aus zwei Einheiten:&lt;br /&gt;
* dem Schieberegister&lt;br /&gt;
* dem Ausgangsregister&lt;br /&gt;
&lt;br /&gt;
Im Schieberegister werden die einzelnen Bits durchgeschoben. Mit jeder positiven Taktflanke(LOW -&amp;gt; HIGH) an &#039;&#039;&#039;SCK&#039;&#039;&#039; wird eine Schiebeoperation durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Das Ausgangsregister hat die Aufgabe die Ausgangspins des Bausteins anzusteuern. Durch dieses Ausgangsregister ist es möglich, die Schiebeoperationen im Hintergrund durchzuführen, ohne dass IC Pins ihren Wert ändern. Erst wenn die Schiebeoperation abgeschlossen ist, wird der aktuelle Zustand der Schieberegisterkette durch einen Puls an &#039;&#039;&#039;RCK&#039;&#039;&#039; in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
===Funktionsweise===&lt;br /&gt;
&lt;br /&gt;
Am Eingang &#039;&#039;&#039;SER&#039;&#039;&#039; (Pin 14) wird das gewünschte nächste Datum (0 oder 1) angelegt. Durch einen positiven Puls an &#039;&#039;&#039;SCK&#039;&#039;&#039; (Pin 1) wird der momentan an &#039;&#039;&#039;SER&#039;&#039;&#039; anliegende Wert als neuer Wert für Bit 0, das unterste Bit des Schieberegisters, übernommen. Gleichzeitig werden alle anderen Bits im Schieberegister um eine Stelle verschoben: Das Bit 6 wird ins Bit 7 übernommen, Bit 5 ins Bit 6, Bit 4 ins Bit 5, etc. sodass das Bit 0 zur Aufnahme des &#039;&#039;&#039;SER&#039;&#039;&#039; Bits frei wird.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-2.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Sonderstellung nimmt das ursprüngliche Bit 7 ein. Dieses Bit steht direkt auch am Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; (Pin 9) zur Verfügung. Dadurch ist es möglich an ein Schieberegister einen weiteren Baustein 74xxx595 anzuschliessen und so beliebig viele Schieberegister hintereinander zu schalten (kaskadieren). Auf diese Art lassen sich Schieberegister mit beliebig vielen Stufen aufbauen.&lt;br /&gt;
&lt;br /&gt;
Wurde das Schieberegister mit den Daten gefüllt, so wird mit einem LOW-HIGH Puls am Pin 12, &#039;&#039;&#039;RCK&#039;&#039;&#039; der Inhalt des Schieberegisters in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-3.png]]&lt;br /&gt;
&lt;br /&gt;
Mit dem Eingang &#039;&#039;&#039;G&#039;&#039;&#039; (Pin 13) kann das Ausgangsregister freigegeben werden. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 0, so führen die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; entsprechende Pegel. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 1, so schalten die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; auf [[Ausgangsstufen Logik-ICs |Tristate]]. D.h. sie treiben aktiv weder LOW oder HIGH, sondern sind hochohmig wie ein Eingänge und nehmen jeden Pegel an, der ihnen von aussen aufgezwungen wird.&lt;br /&gt;
&lt;br /&gt;
Bleibt nur noch der Eingang &#039;&#039;&#039;SCL&#039;&#039;&#039;(Pin 13). Mit ihm kann das Schieberegister im Baustein gelöscht, also auf eine definierte 0, gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Die Programmierung eines 74xxx595 Schieberegisters gestaltet sich sehr einfach. Im Grunde gibt es 2 Möglichkeiten:&lt;br /&gt;
* Mittels [[SPI]] kann der [[AVR]] das Schieberegister direkt und autark ansteuern. Das ist sehr schnell und verbraucht nur wenig CPU-Leistung&lt;br /&gt;
* Sind die entsprechenden SPI-Pins am AVR nicht frei, so ist auch eine softwaremässige Ansteuerung des Schieberegisters mit einfachen Mitteln durchführbar.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software===&lt;br /&gt;
&lt;br /&gt;
Für eine komplette Softwarelösung kann das Schieberegister an jede beliebige Port-Pin Kombination angeschlossen werden. Wir wählen die Pins &#039;&#039;&#039;PB0&#039;&#039;&#039;, &#039;&#039;&#039;PB1&#039;&#039;&#039;, &#039;&#039;&#039;PB2&#039;&#039;&#039; und &#039;&#039;&#039;PB3&#039;&#039;&#039; um dort die Schieberegisteranschlüsse &#039;&#039;&#039;SER&#039;&#039;&#039;, &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;SCL&#039;&#039;&#039; und &#039;&#039;&#039;RCK&#039;&#039;&#039; anzuschliessen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Programmierung gestaltet sich dann nach folgendem Schema: Die 8 Bits eines Bytes werden nacheinander an den Ausgang &#039;&#039;&#039;PB0&#039;&#039;&#039; (&#039;&#039;&#039;SER&#039;&#039;&#039;) ausgegeben. Durch Generierung eines Pulses 0-1-0 an Pin &#039;&#039;&#039;PB1&#039;&#039;&#039; (&#039;&#039;&#039;SCK&#039;&#039;&#039;) übernimmt das Schieberegister nacheinander die einzelnen Bits. Dabei ist zu beachten, dass die Ausgabe mit dem höherwertigen Bit beginnen muss, denn dieses Bit wandert ja am weitesten zur Stelle &#039;&#039;&#039;QH&#039;&#039;&#039;. Sind alle 8 Bits ausgegeben, so wird durch einen weiteren 0-1-0 Impuls am Pin &#039;&#039;&#039;PB3&#039;&#039;&#039; (&#039;&#039;&#039;RCK&#039;&#039;&#039;) der Inhalt der Schieberegisterbits 0 bis 7 in die Ausgaberegister &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; übernommen. Dadurch, dass am Schieberegister der Eingang &#039;&#039;&#039;G&#039;&#039;&#039; konstant auf 0-Pegel gehalten wird, erscheint dann auch die Ausgabe sofort an den entsprechenden Pins und kann zb. mit LEDs (low-current LEDs + Vorwiderstand verwenden) sichtbar gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Der Schieberegistereingang &#039;&#039;&#039;SCL&#039;&#039;&#039; wird auf einer 1 gehalten. Würde er&lt;br /&gt;
auf 0 gehen, so würde die Schieberegisterkette gelöscht. Möchte man einen weiteren Prozessorpin einsparen, so kann man diesen Pin auch generell auf Vcc legen. Das Schieberegister könnte man in so einem Fall durch Einschreiben von 0x00 immer noch löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = 3&lt;br /&gt;
.equ SCK          = 1&lt;br /&gt;
.equ SCL          = 2&lt;br /&gt;
.equ SIN          = 0&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Die Port Pins auf Ausgang konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, (1&amp;lt;&amp;lt;RCK) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;SCL) | (1&amp;lt;&amp;lt;SIN)&lt;br /&gt;
    out   SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; die Clear Leitung am Schieberegister auf 1 stellen&lt;br /&gt;
;&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCL&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe&lt;br /&gt;
    rcall SchiebeOut&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Ausgabe im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
Schiebe:&lt;br /&gt;
    push  temp2&lt;br /&gt;
    ldi   temp2, 8             ; 8 Bits müssen ausgegeben werden&lt;br /&gt;
&lt;br /&gt;
Schiebe_1:&lt;br /&gt;
     ;&lt;br /&gt;
     ; jeweils das höchstwertige Bit aus temp1 ins Carry-Flag schieben&lt;br /&gt;
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend&lt;br /&gt;
     ; gesetzt oder gelöscht&lt;br /&gt;
     ;&lt;br /&gt;
    rol  temp1                 ; MSB -&amp;gt; Carry&lt;br /&gt;
    brcs Schiebe_One           ; Carry gesetzt? -&amp;gt; weiter bei Schiebe_One&lt;br /&gt;
    cbi  SCHIEBE_PORT, SIN     ; Eine 0 ausgeben&lt;br /&gt;
    rjmp Schiebe_Clock         ; und Sprung zur Clock Puls Generierung&lt;br /&gt;
Schiebe_One:&lt;br /&gt;
    sbi  SCHIEBE_PORT, SIN     ; Eine 1 ausgeben&lt;br /&gt;
&lt;br /&gt;
     ;&lt;br /&gt;
     ; einen Impuls an SCK zur Übernahme des Bits nachschieben&lt;br /&gt;
     ;&lt;br /&gt;
Schiebe_Clock:&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCK    ; Clock-Ausgang auf 1 ...&lt;br /&gt;
    cbi   SCHIEBE_PORT, SCK    ; und wieder zurück auf 0&lt;br /&gt;
&lt;br /&gt;
    dec   temp2                ; Anzahl der ausgegebenen Bits runterzählen&lt;br /&gt;
    brne  Schiebe_1            ; Wenn noch keine 8 Bits ausgegeben -&amp;gt; Schleife bilden&lt;br /&gt;
&lt;br /&gt;
    pop   temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul===&lt;br /&gt;
&lt;br /&gt;
Noch schneller geht die Ansteuerung des Schieberegisters mittels [[Serial_Peripheral_Interface | SPI]]-Modul, welches in fast allen AVRs vorhanden ist. Hier wird der Pin &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt, da das praktisch keinen Sinn hat. Er muss also fest auf VCC gelegt werden. Die Pins für &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;SIN&#039;&#039;&#039; sind duch den jeweiligen AVR fest vorgegeben. &#039;&#039;&#039;SCK&#039;&#039;&#039; vom 74xxx595 wird mit &#039;&#039;&#039;SCK&#039;&#039;&#039; vom AVR verbunden sowie &#039;&#039;&#039;SIN&#039;&#039;&#039; mit &#039;&#039;&#039;MOSI&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;O&#039;&#039;&#039;ut, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;I&#039;&#039;&#039;n). &#039;&#039;&#039;MISO&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;I&#039;&#039;&#039;n, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;O&#039;&#039;&#039;ut) ist hier ungenutzt. Es kann NICHT als &#039;&#039;&#039;RCK&#039;&#039;&#039; verwendet werden, da es im SPI-Master Modus immer ein Eingang ist! Es kann aber als allgemeiner Eingang verwendet werden. Der AVR-Pin &#039;&#039;&#039;SS&#039;&#039;&#039; wird sinnvollerweise als &#039;&#039;&#039;RCK&#039;&#039;&#039; benutzt, da er sowieso als Ausgang geschaltet werden &#039;&#039;&#039;muss&#039;&#039;&#039;, sonst gibt es böse Überaschungen (siehe Datenblatt &amp;quot;SS Pin Functionality&amp;quot;). Je nach Bedarf kann man die Taktrate des SPI-Moduls zwischen 1/2 ... 1/128 des CPU-Taktes wählen. Es spricht kaum etwas dagegen mit maximaler Geschwindigkeit zu arbeiten. Die AVRs können zur Zeit mit maximal 20 MHz getaktet werden, d.h. es sind maximal 10 MHz SPI-Takt möglich. Das ist für ein 74xxx595 kein Problem. Die Übertragung von 8 Bit dauert dann gerade mal 800ns!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in    temp1,SCHIEBE_DDR&lt;br /&gt;
    ori   temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out   SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b01010000&lt;br /&gt;
    out   SPCR, temp1           ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   temp1,1&lt;br /&gt;
    out   SPSR,temp1            ; double speed aktivieren&lt;br /&gt;
    out   SPDR,temp1            ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe               ; Daten schieben&lt;br /&gt;
    rcall SchiebeOut            ; Daten in Ausgangsregister übernehmen&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Daten im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
;&lt;br /&gt;
Schiebe:&lt;br /&gt;
    sbis    SPSR,7          ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe&lt;br /&gt;
    out     SPDR,temp1      ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Kaskadieren von Schieberegistern===&lt;br /&gt;
&lt;br /&gt;
Um ein Schieberegister anzuschließen genügen also im einfachsten Fall 4 freie Prozessorpins (3 wenn &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt wird) um weitere 8 Ausgangsleitungen zu bekommen. Genügen diese 8 Leitungen nicht, so kann ohne Probleme ein weiteres Schieberegister an das bereits Vorhandene angeschlossen werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595-2.gif]]&lt;br /&gt;
&lt;br /&gt;
Das nächste Schieberegister wird mit seinem Dateneingang &#039;&#039;&#039;SER&#039;&#039;&#039; einfach an den dafür vorgesehenen Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; des vorhergehenden Schieberegisters angeschlossen. Die Steuerleitungen &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;RCK&#039;&#039;&#039; und &#039;&#039;&#039;SCL&#039;&#039;&#039; werden parallel zu den bereits vorhandenen geschaltet. Konzeptionell erhält man dadurch ein Schieberegister mit einer Breite von 16 Bit. Werden weiter Bausteine in derselben Manier angeschlossen, so erhöht sich die Anzahl der zur Verfügung stehenden Ausgabeleitungen mit jedem Baustein um 8 ohne dass sich die Anzahl der am Prozessor notwendigen Ausgabepins erhöhen würde. Um diese weiteren Register zu nutzen, muss man in der reinen Softwarelösung nur mehrfach die Funktion &#039;&#039;&#039;Schiebe&#039;&#039;&#039; aufrufen, um alle Daten auszugeben. Am Ende werden dann mit &#039;&#039;&#039;SchiebeOut&#039;&#039;&#039; die Daten in die Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
Bei der SPI Lösung werden ebenfalls ganz einfach mehrere Bytes über SPI ausgegeben, ehe dann mittels &#039;&#039;&#039;RCK&#039;&#039;&#039; die in die Schieberegisterkette eingetakteten Bits in das Ausgangsregister übernommen werden.&lt;br /&gt;
Um das Ganze ein wenig zu vereinfachen, soll hier eine Funktion zur Ansteuerung mehrerer kaskadierter Schieberegister über das SPI-Modul gezeigt werden. Dabei wird die Ausgabe mehrerer Bytes über eine Schleife realisiert, mehrfache Aufrufe der Funktion sind damit nicht nötig. Statt dessen übergibt man einen Zeiger auf einen Datenblock im RAM sowie die Anzahl der zu übertragenden Bytes. Ausserdem wird die Datenübernahme durch &#039;&#039;&#039;RCK&#039;&#039;&#039; standardkonform integriert. Denn bei nahezu allen ICs mit SPI wird ein sog. CS-Pin verwendet (&#039;&#039;&#039;C&#039;&#039;&#039;hip &#039;&#039;&#039;S&#039;&#039;&#039;elect) Dieser Pin ist meist LOW aktiv, d.h. wenn er HIGH ist, ignoriert der IC alle Signale an &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;MOSI&#039;&#039;&#039; und gibt keine Daten an MISO aus. Ist er LOW, dann ist der IC aktiv und funktioniert normal. Bei der steigenden Flanke an &#039;&#039;&#039;CS&#039;&#039;&#039; werden die Daten ins Ausgangsregister übernommen. Die Fuktion ist sehr schnell, da die Zeit während der die Übertragung eines Bytes läuft, dazu genutzt wird, den Schleifenzähler zu verringern und zu prüfen sowie neue Sendedaten zu laden. Zwischen den einzelnen Bytes gibt es somit nur eine Pause von max. 6 Systemtakten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org $60&lt;br /&gt;
Schiebedaten:       .byte 2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in      temp1,SCHIEBE_DDR&lt;br /&gt;
    ori     temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out     SCHIEBE_DDR,temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; Slave select inaktiv&lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi     temp1, 0b01010000&lt;br /&gt;
    out     SPCR, temp1         ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi     r16,1&lt;br /&gt;
    out     SPSR,r16            ; Double Speed&lt;br /&gt;
    out     SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; den Datenblock mit Daten füllen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1,$F0&lt;br /&gt;
    sts     Schiebedaten,temp1&lt;br /&gt;
    ldi     temp1,$55&lt;br /&gt;
    sts     Schiebedaten+1,temp1&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
; den Datenblock ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     r16,2&lt;br /&gt;
    ldi     zl,low(Schiebedaten)&lt;br /&gt;
    ldi     zh, high(Schiebedaten)&lt;br /&gt;
    rcall   Schiebe_alle                    ; Daten ausgeben&lt;br /&gt;
&lt;br /&gt;
    rjmp  loop                              ; nur zur Simulation&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes an das Schieberegister ausgeben und in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; r16: Anzahl der Datenbytes&lt;br /&gt;
; Z: Zeiger auf Datenblock im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
Schiebe_alle:&lt;br /&gt;
    cbi     SCHIEBE_PORT, RCK   ; RCK LOW, SPI Standardverfahren&lt;br /&gt;
    push    r17&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_2:&lt;br /&gt;
    ld      r17,Z+&lt;br /&gt;
Schiebe_alle_3:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_3&lt;br /&gt;
    out     SPDR,r17            ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    dec     r16&lt;br /&gt;
    brne    Schiebe_alle_2&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_4:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob die letzte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_4&lt;br /&gt;
&lt;br /&gt;
    pop     r17&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; RCK inaktiv, Datenübernahme&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Nachteil von Schieberegistern ist allerdings, dass sich die Zeit zum Setzten aller Ausgabeleitungen mit jedem weiteren Baustein immer weiter erhöht. Dies deshalb, da ja die einzelnen Bits im Gänsemarsch durch alle Bausteine geschleust werden müssen und für jeden einzelnen Schiebevorgang etwas Zeit notwendig ist. Ein Ausweg ist die Verwendung des SPI-Moduls, welches schneller arbeitet als die reine Softwarelösung. Ist noch mehr Geschwindigkeit gefragt, so sind mehr Port-Pins nötig. Kann ein kompletter Port mit 8 Pins für die Daten genutzt werden, sowie ein paar weitere Steuerleitungen, so können ein oder mehrere 74xxx573 eine Alternative sein, um jeweils ein vollständiges Byte auszugeben. Natürlich kann der 74xxx573 (oder ein ähnliches Schieberegister) auch mit dem 74xxx595 zusammen eingesetzt werden, beispielsweise in dem über das Schieberegister verschiedene 74xxx595 nacheinander aktiviert werden. Weitere Tips und Tricks dazu gibt es vielleicht in einem weiteren Tutorial...&lt;br /&gt;
&lt;br /&gt;
=== Acht LEDs mit je 20mA pro Schieberegister ===&lt;br /&gt;
&lt;br /&gt;
Will man nun acht [[LED]]s mit dem Schieberegister ansteuern, kann man diese direkt über Vorwiderstände anschliessen. Doch ein genauer Blick ins Datenblatt verrät, dass der 74xx595 nur maximal 70mA über VCC bzw. GND ableiten kann. Und wenn man den IC nicht gnadenlos quälen, und damit die Lebensdauer und Zuverlässigkeit drastisch reduzieren will, gibt es nur zwei Auswege.&lt;br /&gt;
&lt;br /&gt;
* Den Strom pro LED auf 70/8 = 8,75mA begrenzen; Das reicht meistens aus um die LEDs schön leuchten zu lassen, vor allem bei low-current und ultrahellen LEDs&lt;br /&gt;
* Wenn doch 20 mA pro LED gebraucht werden, kann man die folgende Trickschaltung anwenden.&lt;br /&gt;
&lt;br /&gt;
[[bild:8x20mA_LED_mit_74xx595.png]]&lt;br /&gt;
&lt;br /&gt;
Der Trick besteht darin, dass 4 LEDs ihren Strom über das Schieberegister von VCC beziehen (HIGH aktiv) während die anderen vier ihren Strom über GND leiten (LOW aktiv). Damit bleiben ganz offiziell für jede LED 70/4 = 17,5mA. Um die Handhabung in der Software zu vereinfachen muss nur vor der Ausgabe der Daten das jeweilige Byte mit 0x0F XOR verknüpft werden, bevor es in das Schieberegister getaktet wird. Dadurch werden die LOW-aktiven LEDs richtig angesteuert und die Datenhandhabung in der Software muss nur mit HIGH-aktiven rechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Achtung!&amp;lt;/B&amp;gt; Die Widerstände sind auf blaue LEDs mit 3,3V Flusspannung ausgelegt. Bei roten, gelben und grünen [[LED]]s ist die Flusspannung geringer und dementsprechend muss der Vorwiderstand grösser sein.&lt;br /&gt;
&lt;br /&gt;
Ausserdem wird der G Eingang verwendet, um die Helligkeit aller LEDs per [[PWM]] zu steuern. Beachtet werden muss, dass die PWM im invertierten Modus generiert werden muss, da der Eingang G LOW aktiv ist.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Eingänge ==&lt;br /&gt;
&lt;br /&gt;
Ein naher Verwandter des 75xx595 ist der 74xx165, er ist quasi das Gegenstück. Hierbei handet es sich um ein &#039;&#039;8-bit parallel-in/serial-out shift register&#039;&#039;. Auf deutsch ein 8 Bit Schieberegister mit parallelem Eingang und seriellem Ausgang. Damit kann man ein grosse Anzahl Eingänge sehr einfach und preiswert zu seinem Mikrocontroller hinzufügen.&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-1.png]]&lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist sehr ähnlich zum 74xx595. Allerdings gibt es kein Register zum Zwischenspeichern. Das ist auch gar nicht nötig, da der IC ja einen parallelen Eingang hat. Der muss nicht zwischengespeichert werden. Es gibt hier also wirklich nur das Schieberegister. Dieses wird über den Eingang PL mit den parallelen Daten geladen. Dann können die Daten seriell mit Takten an CLK aus dem Ausgang Q7 geschoben werden.&lt;br /&gt;
 &lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
DS ist der serielle Dateneingang, welcher im Falle von kaskadierten Schieberegistern mit dem Ausgang des vorhergehenden ICs verbunden wird.&lt;br /&gt;
&lt;br /&gt;
D0..D7 sind die parallelen Dateneingänge.&lt;br /&gt;
&lt;br /&gt;
Mittels des Eingangs PL (&#039;&#039;&#039;P&#039;&#039;&#039;arallel &#039;&#039;&#039;L&#039;&#039;&#039;oad) werden die Daten vom parallelen Eingang in das Schieberegister übernommen, wenn dieses Signal LOW ist. Hier muss man aber ein klein wenig aufpassen. Auf grund der Schaltungsstruktur ist der Eingang PL mit dem Takt CLK verknüpft (obwohl es dafür keinen logischen Grund gibt :-0). Damit es nicht zu unerwünschten Fehlschaltungen kommt, muss der Takt CLK während des Ladens auf HIGH liegen. Wird PL wieder auf HIGH gesetzt, sind die Daten geladen. Das erste Bit liegt direkt am Ausgang Q7 and. Die restlichen Bits können nach und nach durch das Register geschoben werden.&lt;br /&gt;
&lt;br /&gt;
Der Eingang CE (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;E&#039;&#039;&#039;nable) steuert, ob das Schieberegister auf den Takt CLK reagieren soll oder nicht. Ist CE gleich LOW werden alle Takte an CLK ignoriert. Bei HIGH werden mit jeder positiven Flanke die Daten um eine Stufe weiter geschoben.&lt;br /&gt;
&lt;br /&gt;
Wird am Eingang CLK eine LOW-HIGH Flanke angelegt und ist dabei CE auf LOW, dann werden die Daten im Schieberegister um eine Position weiter geschoben: DS-&amp;gt;Q0, Q0-&amp;gt;Q1, Q1-&amp;gt;Q2, Q2-&amp;gt;Q3, Q3-&amp;gt;Q4, Q4-&amp;gt;Q5, Q5-&amp;gt;Q6, Q6-&amp;gt;Q7. Q0..Q6 sind interne Signale, siehe [http://www.nxp.com/acrobat/datasheets/74HC_HCT165_CNV_2.pdf Datenblatt].&lt;br /&gt;
&lt;br /&gt;
Q7 ist der Ausgang des Schieberegisters. Dort Werden Takt für Takt die Daten ausgeggeben. Hier wird normalerweise der Eingang des Mikrocontrollers oder der Eingang des nächsten Schieberegisters angeschlossen.&lt;br /&gt;
&lt;br /&gt;
Q7\ ist der invertierte Ausgang des Schieberegisters. Er wird meist nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Schaltung ===&lt;br /&gt;
&lt;br /&gt;
Um nun beispielsweise zwei Schieberegister zu kaskadieren um 16 Eingangspins zu erhalten sollte man folgende Verschaltung vornehmen. Beachten sollte man dabei, dass&lt;br /&gt;
&lt;br /&gt;
* der serielle Eingang DS des ersten Schieberegisters (hier IC1) auf einen festen Pegel gelegt wird (LOW oder HIGH).&lt;br /&gt;
* der serielle Datenausgang bei der Benutzung des SPI-Moduls an MISO und nicht an MOSI angeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-2.png]]&lt;br /&gt;
&lt;br /&gt;
Nachfolgend werden zwei Beispiele gezeigt, welche die Ansteuerung nach bekanntem Muster übernehemn. Nur dass hier eben Daten gelesen anstatt geschrieben werden. Zu beachten ist, dass hier ein anderer Modus der SPI-Ansteuerung verwendet werden muss, weil der Baustein das nötig macht. Das muss beachtet werden, wenn auch Schieberegister für Ausgänge verwendet werden. Dabei muss jeweils vor dem Zugriff auf die Ein- oder Ausgangsregister der Modus des Taktes (CPOL) umgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per Software&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen, frei wählbar&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ SCHIEBE_PIN  = PINB&lt;br /&gt;
.equ CLK          = PB3&lt;br /&gt;
.equ PL           = PB1&lt;br /&gt;
.equ DIN          = PB2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:		.byte 2				; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
	ldi		temp1, LOW(RAMEND)	; Stackpointer initialisieren&lt;br /&gt;
    out		SPL, temp1&lt;br /&gt;
    ldi		temp1, HIGH(RAMEND)&lt;br /&gt;
    out 	SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi   	temp1, (1&amp;lt;&amp;lt;clk) | (1&amp;lt;&amp;lt;pl)&lt;br /&gt;
    out   	SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
	sbi		schiebe_port, clk	; Takt im Ruhezustand immer auf 1&lt;br /&gt;
								; komische Schaltung im 74xx165&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi		ZL,low(Daten)&lt;br /&gt;
    ldi		ZH,high(Daten)&lt;br /&gt;
	ldi		temp1,2&lt;br /&gt;
    rcall	schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  	loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push	temp2				; Register sichern&lt;br /&gt;
	push	temp3&lt;br /&gt;
&lt;br /&gt;
	cbi		schiebe_port, pl	; Daten parallel laden&lt;br /&gt;
	sbi		schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
&lt;br /&gt;
	ldi		temp3, 8			; Bitzähler&lt;br /&gt;
schiebe_eingang_bit_schleife:&lt;br /&gt;
	lsl		temp2				; Daten weiterschieben&lt;br /&gt;
&lt;br /&gt;
; das IO Bit Din in das niederwerigste Bit von temp2 kopieren&lt;br /&gt;
&lt;br /&gt;
 	sbic	schiebe_pin, din&lt;br /&gt;
	ori		temp2,1				; Bit setzen&lt;br /&gt;
	sbis	schiebe_pin, din&lt;br /&gt;
	andi	temp2, ~1			; Bit löschen&lt;br /&gt;
&lt;br /&gt;
    cbi   	SCHIEBE_PORT, CLK   ; Taktausgang auf 0&lt;br /&gt;
    sbi   	SCHIEBE_PORT, CLK   ; und wieder zurück auf 1, dabei Daten schieben &lt;br /&gt;
&lt;br /&gt;
    dec   	temp3             	; Bitzähler um eins verringern&lt;br /&gt;
    brne  	schiebe_eingang_bit_schleife ;wenn noch keine 8 Bits ausgegeben, nochmal&lt;br /&gt;
&lt;br /&gt;
	st		z+,temp2			; Datenbyte speichern&lt;br /&gt;
	dec		temp1				; Anzahl Bytes um eins verringern&lt;br /&gt;
	brne	schiebe_eingang_byte_schleife	; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
	pop		temp3&lt;br /&gt;
    pop		temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per SPI-Modul&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen&lt;br /&gt;
; diese müssen mit den SPI-Pins des AVR Typs übereinstimmen!&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ PL           = PB2     	; SS&lt;br /&gt;
.equ CLK          = PB5     	; SCK&lt;br /&gt;
.equ DIN          = PB4     	; MISO&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:		.byte 2				; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
	ldi		temp1, LOW(RAMEND)	; Stackpointer initialisieren&lt;br /&gt;
    out		SPL, temp1&lt;br /&gt;
    ldi		temp1, HIGH(RAMEND)&lt;br /&gt;
    out 	SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi   	temp1,(1&amp;lt;&amp;lt;CLK) | (1&amp;lt;&amp;lt;PL)&lt;br /&gt;
    out   	SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   	temp1, 0b01011000&lt;br /&gt;
    out   	SPCR, temp1       	; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 1, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   	temp1,1&lt;br /&gt;
    out   	SPSR,temp1        	; double speed aktivieren&lt;br /&gt;
    out   	SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi		ZL,low(Daten)&lt;br /&gt;
    ldi		ZH,high(Daten)&lt;br /&gt;
	ldi		temp1,2&lt;br /&gt;
    rcall	schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  	loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push	temp2				; Register sichern&lt;br /&gt;
&lt;br /&gt;
	; CLK ist im Ruhezustand schon auf HIGH, CPOL=1&lt;br /&gt;
&lt;br /&gt;
	cbi		schiebe_port, pl	; Daten parallel laden&lt;br /&gt;
	sbi		schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_1:&lt;br /&gt;
	sbis    SPSR,7          	; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    schiebe_eingang_1&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
    out     SPDR,temp1      	; beliebige Daten ins SPI Modul schreiben&lt;br /&gt;
								; um die Übertragung zu starten&lt;br /&gt;
schiebe_eingang_2:&lt;br /&gt;
	sbis    SPSR,7          	; auf das Ende der Übertragung warten&lt;br /&gt;
    rjmp    schiebe_eingang_2&lt;br /&gt;
&lt;br /&gt;
	in		temp2, spdr			; Daten lesen&lt;br /&gt;
	st		z+,temp2			; Datenbyte speichern&lt;br /&gt;
	dec		temp1				; Anzahl Bytes um eins verringern&lt;br /&gt;
	brne	schiebe_eingang_byte_schleife	; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
    pop		temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
AVR Studio 4.12 (Build 498) hat Probleme bei der korrekten Simulation des SPI-Moduls.&lt;br /&gt;
&lt;br /&gt;
* Der Double-Speed Modus funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
* Das Bit SPIF im Register SPSR, welches laut Dokumentation nur lesbar ist, ist im Simulator auch schreibbar! Das kann zu Verwirrung und Fehlern in der Simulation führen.&lt;br /&gt;
&lt;br /&gt;
Hardwareprobleme&lt;br /&gt;
&lt;br /&gt;
* Wenn das SPI-Modul aktiviert wird, wird &#039;&#039;&#039;NICHT&#039;&#039;&#039; automatisch SPIF gesetzt, es bleibt auf Null. Damit würde die erste Abfrage in &#039;&#039;Schiebe_alles&#039;&#039; in einer Endlosschleife hängen bleiben. Deshalb muss nach der Initialisierung des SPI-Moduls ein Dummy Byte gesendet werden, damit am Ende der Übertragung SPIF gesetzt wird&lt;br /&gt;
&lt;br /&gt;
* Da das SPI-Modul in Senderichtung nur einfach gepuffert ist, ist es nicht möglich absolut lückenlos Daten zu senden, auch wenn man mit &#039;&#039;&#039;nop&#039;&#039;&#039; eine feste minimale Zeit zwischen zwei Bytes warten würde. Zwischen zwei Bytes muss immer eine Pause von mind. 2 Systemtakten eingehalten werden.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=PWM|&lt;br /&gt;
zurücklink=AVR-Tutorial: PWM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=SRAM|&lt;br /&gt;
vorlink=AVR-Tutorial: SRAM}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25437</id>
		<title>AVR-Tutorial: Schieberegister</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25437"/>
		<updated>2008-01-08T22:39:41Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Artikel erweitert um Eingangsregister 74xx165&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ab und an stellt sich folgendes Problem: Man würde wesentlich mehr Ausgangspins oder Eingangspins benötigen als der [[Mikrocontroller]] zur Verfügung stellt. Ein möglicher Ausweg ist eine Porterweiterung mit einem Schieberegister. Zwei beliebte Schieberegister sind beispielsweise der 74xx595 bzw. der 74xx165.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Ausgänge ==&lt;br /&gt;
&lt;br /&gt;
Um neue Ausgangspins zu gewinnen kann der 74xx595 verwendet werden. Dabei handelt es sich um ein &#039;&#039;8-Bit 3-state Serial-in/Serial-out or Parallel-Out Schieberegister mit einem Ausgangsregister und einem asynchronen Reset&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Hinter dieser kompliziert anmutenden Beschreibung verbirgt sich eine einfache Funktionalität: Das Schieberegister besteht aus zwei Funktionseinheiten: Dem eigentlichen Schieberegister und dem Ausgangsregister. In das Schieberegister können die Daten seriell hineingetaktet werden und durch ein bestimmtes Signal werden die Daten des Schieberegisters in das Ausgangsregister übernommen und können von dort auf die Ausgangspins geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Im Einzelnen bedeuten die Begriffe:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Begriff || Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|8-Bit   || Acht Ausgangs[[Bit|bit]]s&lt;br /&gt;
|-&lt;br /&gt;
|3-state || Die acht Registerausgänge können drei Zustände, Low, High und High-Impedanz annehmen.&amp;lt;BR&amp;gt;Siehe [[Ausgangsstufen Logik-ICs]]&lt;br /&gt;
|-&lt;br /&gt;
|Serial-in || Serieller Eingang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Serial-out || Serieller Ausgang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Parallel-Out || Parallele Ausgänge des Ausgangsregisters&lt;br /&gt;
|-&lt;br /&gt;
|Schieberegister || Serielle Daten werden durch den Baustein durchgeschoben&lt;br /&gt;
|-&lt;br /&gt;
|Ausgangsregister || Ein Speicher, welcher die Daten des Schieberegisters zwischenspeichern kann.&amp;lt;BR&amp;gt;Dieses besteht aus acht [[FlipFlop]]s.&lt;br /&gt;
|-&lt;br /&gt;
|Asynchroner Reset || Die Daten im Schieberegister können asynchron zum zurückgesetzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-1.png]]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Die Benennung der Pins in den Datenblättern verschiedener Hersteller unterscheidet sich zum Teil. Die Funktionen der Pins sind jedoch gleich.&lt;br /&gt;
&lt;br /&gt;
{| cellspacing=”0” border=&amp;quot;1&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele von Hersteller-Pinbenennungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! DIL Pin-Nummer || Funktion || Dieses Tutorial || Motorola / ON Semi || Philips         || Fairchild     || SGS|| Texas Instruments&lt;br /&gt;
|-&lt;br /&gt;
|  1 || Ausgang B           || QB || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || QB|| Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  2 || Ausgang C           || QC || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || QC|| Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  3 || Ausgang D           || QD || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || QD|| Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  4 || Ausgang E           || QE || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || QE|| Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  5 || Ausgang F           || QF || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || QF|| Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  6 || Ausgang G           || QG || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;6&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || QG|| Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  7 || Ausgang H           || QH || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || QH|| Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  8 || Masse, 0 V || [nicht dargestellt] || GND            || GND             || GND             || GND|| GND&lt;br /&gt;
|-&lt;br /&gt;
|  9 || Serieller Ausgang || QH* || SQ&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;´  ||Q&#039;&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  ||  QH´||Q&amp;lt;sub&amp;gt;H&#039;&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 10 || Reset für Schieberegister             || SCL || RESET          || /MR             || /SCLR        || /SCLR || /SRCLR&lt;br /&gt;
|-&lt;br /&gt;
| 11 || Schiebetakt        || SCK || SHIFT CLOCK    || SH&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || SCK          || SCK || SRCLK&lt;br /&gt;
|-&lt;br /&gt;
| 12 || Speichertakt        || RCK || LATCH CLOCK    || ST&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || RCK          || RCK || RCLK&lt;br /&gt;
|-&lt;br /&gt;
| 13 || Ausgangssteuerung      || G || OUTPUT ENABLE  || /OE             || /G           || /G || /OE&lt;br /&gt;
|-&lt;br /&gt;
| 14 || Serieller Dateneingang  || SER || A              || D&amp;lt;sub&amp;gt;S&amp;lt;/sub&amp;gt;   || SER          || SI || SER&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Ausgang A           || QA || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || QA|| Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 16 || Betriebsspannung || [nicht dargestellt] || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;|| V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Baustein besteht aus zwei Einheiten:&lt;br /&gt;
* dem Schieberegister&lt;br /&gt;
* dem Ausgangsregister&lt;br /&gt;
&lt;br /&gt;
Im Schieberegister werden die einzelnen Bits durchgeschoben. Mit jeder positiven Taktflanke(LOW -&amp;gt; HIGH) an &#039;&#039;&#039;SCK&#039;&#039;&#039; wird eine Schiebeoperation durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Das Ausgangsregister hat die Aufgabe die Ausgangspins des Bausteins anzusteuern. Durch dieses Ausgangsregister ist es möglich, die Schiebeoperationen im Hintergrund durchzuführen, ohne dass IC Pins ihren Wert ändern. Erst wenn die Schiebeoperation abgeschlossen ist, wird der aktuelle Zustand der Schieberegisterkette durch einen Puls an &#039;&#039;&#039;RCK&#039;&#039;&#039; in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
===Funktionsweise===&lt;br /&gt;
&lt;br /&gt;
Am Eingang &#039;&#039;&#039;SER&#039;&#039;&#039; (Pin 14) wird das gewünschte nächste Datum (0 oder 1) angelegt. Durch einen positiven Puls an &#039;&#039;&#039;SCK&#039;&#039;&#039; (Pin 1) wird der momentan an &#039;&#039;&#039;SER&#039;&#039;&#039; anliegende Wert als neuer Wert für Bit 0, das unterste Bit des Schieberegisters, übernommen. Gleichzeitig werden alle anderen Bits im Schieberegister um eine Stelle verschoben: Das Bit 6 wird ins Bit 7 übernommen, Bit 5 ins Bit 6, Bit 4 ins Bit 5, etc. sodass das Bit 0 zur Aufnahme des &#039;&#039;&#039;SER&#039;&#039;&#039; Bits frei wird.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-2.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Sonderstellung nimmt das ursprüngliche Bit 7 ein. Dieses Bit steht direkt auch am Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; (Pin 9) zur Verfügung. Dadurch ist es möglich an ein Schieberegister einen weiteren Baustein 74xxx595 anzuschliessen und so beliebig viele Schieberegister hintereinander zu schalten (kaskadieren). Auf diese Art lassen sich Schieberegister mit beliebig vielen Stufen aufbauen.&lt;br /&gt;
&lt;br /&gt;
Wurde das Schieberegister mit den Daten gefüllt, so wird mit einem LOW-HIGH Puls am Pin 12, &#039;&#039;&#039;RCK&#039;&#039;&#039; der Inhalt des Schieberegisters in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-3.png]]&lt;br /&gt;
&lt;br /&gt;
Mit dem Eingang &#039;&#039;&#039;G&#039;&#039;&#039; (Pin 13) kann das Ausgangsregister freigegeben werden. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 0, so führen die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; entsprechende Pegel. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 1, so schalten die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; auf [[Ausgangsstufen Logik-ICs |Tristate]]. D.h. sie treiben aktiv weder LOW oder HIGH, sondern sind hochohmig wie ein Eingänge und nehmen jeden Pegel an, der ihnen von aussen aufgezwungen wird.&lt;br /&gt;
&lt;br /&gt;
Bleibt nur noch der Eingang &#039;&#039;&#039;SCL&#039;&#039;&#039;(Pin 13). Mit ihm kann das Schieberegister im Baustein gelöscht, also auf eine definierte 0, gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Die Programmierung eines 74xxx595 Schieberegisters gestaltet sich sehr einfach. Im Grunde gibt es 2 Möglichkeiten:&lt;br /&gt;
* Mittels [[SPI]] kann der [[AVR]] das Schieberegister direkt und autark ansteuern. Das ist sehr schnell und verbraucht nur wenig CPU-Leistung&lt;br /&gt;
* Sind die entsprechenden SPI-Pins am AVR nicht frei, so ist auch eine softwaremässige Ansteuerung des Schieberegisters mit einfachen Mitteln durchführbar.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software===&lt;br /&gt;
&lt;br /&gt;
Für eine komplette Softwarelösung kann das Schieberegister an jede beliebige Port-Pin Kombination angeschlossen werden. Wir wählen die Pins &#039;&#039;&#039;PB0&#039;&#039;&#039;, &#039;&#039;&#039;PB1&#039;&#039;&#039;, &#039;&#039;&#039;PB2&#039;&#039;&#039; und &#039;&#039;&#039;PB3&#039;&#039;&#039; um dort die Schieberegisteranschlüsse &#039;&#039;&#039;SER&#039;&#039;&#039;, &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;SCL&#039;&#039;&#039; und &#039;&#039;&#039;RCK&#039;&#039;&#039; anzuschliessen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Programmierung gestaltet sich dann nach folgendem Schema: Die 8 Bits eines Bytes werden nacheinander an den Ausgang &#039;&#039;&#039;PB0&#039;&#039;&#039; (&#039;&#039;&#039;SER&#039;&#039;&#039;) ausgegeben. Durch Generierung eines Pulses 0-1-0 an Pin &#039;&#039;&#039;PB1&#039;&#039;&#039; (&#039;&#039;&#039;SCK&#039;&#039;&#039;) übernimmt das Schieberegister nacheinander die einzelnen Bits. Dabei ist zu beachten, dass die Ausgabe mit dem höherwertigen Bit beginnen muss, denn dieses Bit wandert ja am weitesten zur Stelle &#039;&#039;&#039;QH&#039;&#039;&#039;. Sind alle 8 Bits ausgegeben, so wird durch einen weiteren 0-1-0 Impuls am Pin &#039;&#039;&#039;PB3&#039;&#039;&#039; (&#039;&#039;&#039;RCK&#039;&#039;&#039;) der Inhalt der Schieberegisterbits 0 bis 7 in die Ausgaberegister &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; übernommen. Dadurch, dass am Schieberegister der Eingang &#039;&#039;&#039;G&#039;&#039;&#039; konstant auf 0-Pegel gehalten wird, erscheint dann auch die Ausgabe sofort an den entsprechenden Pins und kann zb. mit LEDs (low-current LEDs + Vorwiderstand verwenden) sichtbar gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Der Schieberegistereingang &#039;&#039;&#039;SCL&#039;&#039;&#039; wird auf einer 1 gehalten. Würde er&lt;br /&gt;
auf 0 gehen, so würde die Schieberegisterkette gelöscht. Möchte man einen weiteren Prozessorpin einsparen, so kann man diesen Pin auch generell auf Vcc legen. Das Schieberegister könnte man in so einem Fall durch Einschreiben von 0x00 immer noch löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = 3&lt;br /&gt;
.equ SCK          = 1&lt;br /&gt;
.equ SCL          = 2&lt;br /&gt;
.equ SIN          = 0&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Die Port Pins auf Ausgang konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, (1&amp;lt;&amp;lt;RCK) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;SCL) | (1&amp;lt;&amp;lt;SIN)&lt;br /&gt;
    out   SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; die Clear Leitung am Schieberegister auf 1 stellen&lt;br /&gt;
;&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCL&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe&lt;br /&gt;
    rcall SchiebeOut&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Ausgabe im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
Schiebe:&lt;br /&gt;
    push  temp2&lt;br /&gt;
    ldi   temp2, 8             ; 8 Bits müssen ausgegeben werden&lt;br /&gt;
&lt;br /&gt;
Schiebe_1:&lt;br /&gt;
     ;&lt;br /&gt;
     ; jeweils das höchstwertige Bit aus temp1 ins Carry-Flag schieben&lt;br /&gt;
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend&lt;br /&gt;
     ; gesetzt oder gelöscht&lt;br /&gt;
     ;&lt;br /&gt;
    rol  temp1                 ; MSB -&amp;gt; Carry&lt;br /&gt;
    brcs Schiebe_One           ; Carry gesetzt? -&amp;gt; weiter bei Schiebe_One&lt;br /&gt;
    cbi  SCHIEBE_PORT, SIN     ; Eine 0 ausgeben&lt;br /&gt;
    rjmp Schiebe_Clock         ; und Sprung zur Clock Puls Generierung&lt;br /&gt;
Schiebe_One:&lt;br /&gt;
    sbi  SCHIEBE_PORT, SIN     ; Eine 1 ausgeben&lt;br /&gt;
&lt;br /&gt;
     ;&lt;br /&gt;
     ; einen Impuls an SCK zur Übernahme des Bits nachschieben&lt;br /&gt;
     ;&lt;br /&gt;
Schiebe_Clock:&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCK    ; Clock-Ausgang auf 1 ...&lt;br /&gt;
    cbi   SCHIEBE_PORT, SCK    ; und wieder zurück auf 0&lt;br /&gt;
&lt;br /&gt;
    dec   temp2                ; Anzahl der ausgegebenen Bits runterzählen&lt;br /&gt;
    brne  Schiebe_1            ; Wenn noch keine 8 Bits ausgegeben -&amp;gt; Schleife bilden&lt;br /&gt;
&lt;br /&gt;
    pop   temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul===&lt;br /&gt;
&lt;br /&gt;
Noch schneller geht die Ansteuerung des Schieberegisters mittels [[Serial_Peripheral_Interface | SPI]]-Modul, welches in fast allen AVRs vorhanden ist. Hier wird der Pin &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt, da das praktisch keinen Sinn hat. Er muss also fest auf VCC gelegt werden. Die Pins für &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;SIN&#039;&#039;&#039; sind duch den jeweiligen AVR fest vorgegeben. &#039;&#039;&#039;SCK&#039;&#039;&#039; vom 74xxx595 wird mit &#039;&#039;&#039;SCK&#039;&#039;&#039; vom AVR verbunden sowie &#039;&#039;&#039;SIN&#039;&#039;&#039; mit &#039;&#039;&#039;MOSI&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;O&#039;&#039;&#039;ut, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;I&#039;&#039;&#039;n). &#039;&#039;&#039;MISO&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;I&#039;&#039;&#039;n, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;O&#039;&#039;&#039;ut) ist hier ungenutzt. Es kann NICHT als &#039;&#039;&#039;RCK&#039;&#039;&#039; verwendet werden, da es im SPI-Master Modus immer ein Eingang ist! Es kann aber als allgemeiner Eingang verwendet werden. Der AVR-Pin &#039;&#039;&#039;SS&#039;&#039;&#039; wird sinnvollerweise als &#039;&#039;&#039;RCK&#039;&#039;&#039; benutzt, da er sowieso als Ausgang geschaltet werden &#039;&#039;&#039;muss&#039;&#039;&#039;, sonst gibt es böse Überaschungen (siehe Datenblatt &amp;quot;SS Pin Functionality&amp;quot;). Je nach Bedarf kann man die Taktrate des SPI-Moduls zwischen 1/2 ... 1/128 des CPU-Taktes wählen. Es spricht kaum etwas dagegen mit maximaler Geschwindigkeit zu arbeiten. Die AVRs können zur Zeit mit maximal 20 MHz getaktet werden, d.h. es sind maximal 10 MHz SPI-Takt möglich. Das ist für ein 74xxx595 kein Problem. Die Übertragung von 8 Bit dauert dann gerade mal 800ns!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in    temp1,SCHIEBE_DDR&lt;br /&gt;
    ori   temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out   SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b01010000&lt;br /&gt;
    out   SPCR, temp1           ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   temp1,1&lt;br /&gt;
    out   SPSR,temp1            ; double speed aktivieren&lt;br /&gt;
    out   SPDR,temp1            ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe               ; Daten schieben&lt;br /&gt;
    rcall SchiebeOut            ; Daten in Ausgangsregister übernehmen&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Daten im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
;&lt;br /&gt;
Schiebe:&lt;br /&gt;
    sbis    SPSR,7          ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe&lt;br /&gt;
    out     SPDR,temp1      ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Kaskadieren von Schieberegistern===&lt;br /&gt;
&lt;br /&gt;
Um ein Schieberegister anzuschließen genügen also im einfachsten Fall 4 freie Prozessorpins (3 wenn &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt wird) um weitere 8 Ausgangsleitungen zu bekommen. Genügen diese 8 Leitungen nicht, so kann ohne Probleme ein weiteres Schieberegister an das bereits Vorhandene angeschlossen werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595-2.gif]]&lt;br /&gt;
&lt;br /&gt;
Das nächste Schieberegister wird mit seinem Dateneingang &#039;&#039;&#039;SER&#039;&#039;&#039; einfach an den dafür vorgesehenen Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; des vorhergehenden Schieberegisters angeschlossen. Die Steuerleitungen &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;RCK&#039;&#039;&#039; und &#039;&#039;&#039;SCL&#039;&#039;&#039; werden parallel zu den bereits vorhandenen geschaltet. Konzeptionell erhält man dadurch ein Schieberegister mit einer Breite von 16 Bit. Werden weiter Bausteine in derselben Manier angeschlossen, so erhöht sich die Anzahl der zur Verfügung stehenden Ausgabeleitungen mit jedem Baustein um 8 ohne dass sich die Anzahl der am Prozessor notwendigen Ausgabepins erhöhen würde. Um diese weiteren Register zu nutzen, muss man in der reinen Softwarelösung nur mehrfach die Funktion &#039;&#039;&#039;Schiebe&#039;&#039;&#039; aufrufen, um alle Daten auszugeben. Am Ende werden dann mit &#039;&#039;&#039;SchiebeOut&#039;&#039;&#039; die Daten in die Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
Bei der SPI Lösung werden ebenfalls ganz einfach mehrere Bytes über SPI ausgegeben, ehe dann mittels &#039;&#039;&#039;RCK&#039;&#039;&#039; die in die Schieberegisterkette eingetakteten Bits in das Ausgangsregister übernommen werden.&lt;br /&gt;
Um das Ganze ein wenig zu vereinfachen, soll hier eine Funktion zur Ansteuerung mehrerer kaskadierter Schieberegister über das SPI-Modul gezeigt werden. Dabei wird die Ausgabe mehrerer Bytes über eine Schleife realisiert, mehrfache Aufrufe der Funktion sind damit nicht nötig. Statt dessen übergibt man einen Zeiger auf einen Datenblock im RAM sowie die Anzahl der zu übertragenden Bytes. Ausserdem wird die Datenübernahme durch &#039;&#039;&#039;RCK&#039;&#039;&#039; standardkonform integriert. Denn bei nahezu allen ICs mit SPI wird ein sog. CS-Pin verwendet (&#039;&#039;&#039;C&#039;&#039;&#039;hip &#039;&#039;&#039;S&#039;&#039;&#039;elect) Dieser Pin ist meist LOW aktiv, d.h. wenn er HIGH ist, ignoriert der IC alle Signale an &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;MOSI&#039;&#039;&#039; und gibt keine Daten an MISO aus. Ist er LOW, dann ist der IC aktiv und funktioniert normal. Bei der steigenden Flanke an &#039;&#039;&#039;CS&#039;&#039;&#039; werden die Daten ins Ausgangsregister übernommen. Die Fuktion ist sehr schnell, da die Zeit während der die Übertragung eines Bytes läuft, dazu genutzt wird, den Schleifenzähler zu verringern und zu prüfen sowie neue Sendedaten zu laden. Zwischen den einzelnen Bytes gibt es somit nur eine Pause von max. 6 Systemtakten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org $60&lt;br /&gt;
Schiebedaten:       .byte 2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in      temp1,SCHIEBE_DDR&lt;br /&gt;
    ori     temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out     SCHIEBE_DDR,temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; Slave select inaktiv&lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi     temp1, 0b01010000&lt;br /&gt;
    out     SPCR, temp1         ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi     r16,1&lt;br /&gt;
    out     SPSR,r16            ; Double Speed&lt;br /&gt;
    out     SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; den Datenblock mit Daten füllen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1,$F0&lt;br /&gt;
    sts     Schiebedaten,temp1&lt;br /&gt;
    ldi     temp1,$55&lt;br /&gt;
    sts     Schiebedaten+1,temp1&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
; den Datenblock ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     r16,2&lt;br /&gt;
    ldi     zl,low(Schiebedaten)&lt;br /&gt;
    ldi     zh, high(Schiebedaten)&lt;br /&gt;
    rcall   Schiebe_alle                    ; Daten ausgeben&lt;br /&gt;
&lt;br /&gt;
    rjmp  loop                              ; nur zur Simulation&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes an das Schieberegister ausgeben und in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; r16: Anzahl der Datenbytes&lt;br /&gt;
; Z: Zeiger auf Datenblock im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
Schiebe_alle:&lt;br /&gt;
    cbi     SCHIEBE_PORT, RCK   ; RCK LOW, SPI Standardverfahren&lt;br /&gt;
    push    r17&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_2:&lt;br /&gt;
    ld      r17,Z+&lt;br /&gt;
Schiebe_alle_3:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_3&lt;br /&gt;
    out     SPDR,r17            ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    dec     r16&lt;br /&gt;
    brne    Schiebe_alle_2&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_4:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob die letzte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_4&lt;br /&gt;
&lt;br /&gt;
    pop     r17&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; RCK inaktiv, Datenübernahme&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Nachteil von Schieberegistern ist allerdings, dass sich die Zeit zum Setzten aller Ausgabeleitungen mit jedem weiteren Baustein immer weiter erhöht. Dies deshalb, da ja die einzelnen Bits im Gänsemarsch durch alle Bausteine geschleust werden müssen und für jeden einzelnen Schiebevorgang etwas Zeit notwendig ist. Ein Ausweg ist die Verwendung des SPI-Moduls, welches schneller arbeitet als die reine Softwarelösung. Ist noch mehr Geschwindigkeit gefragt, so sind mehr Port-Pins nötig. Kann ein kompletter Port mit 8 Pins für die Daten genutzt werden, sowie ein paar weitere Steuerleitungen, so können ein oder mehrere 74xxx573 eine Alternative sein, um jeweils ein vollständiges Byte auszugeben. Natürlich kann der 74xxx573 (oder ein ähnliches Schieberegister) auch mit dem 74xxx595 zusammen eingesetzt werden, beispielsweise in dem über das Schieberegister verschiedene 74xxx595 nacheinander aktiviert werden. Weitere Tips und Tricks dazu gibt es vielleicht in einem weiteren Tutorial...&lt;br /&gt;
&lt;br /&gt;
=== Acht LEDs mit je 20mA pro Schieberegister ===&lt;br /&gt;
&lt;br /&gt;
Will man nun acht [[LED]]s mit dem Schieberegister ansteuern, kann man diese direkt über Vorwiderstände anschliessen. Doch ein genauer Blick ins Datenblatt verrät, dass der 74xx595 nur maximal 70mA über VCC bzw. GND ableiten kann. Und wenn man den IC nicht gnadenlos quälen, und damit die Lebensdauer und Zuverlässigkeit drastisch reduzieren will, gibt es nur zwei Auswege.&lt;br /&gt;
&lt;br /&gt;
* Den Strom pro LED auf 70/8 = 8,75mA begrenzen; Das reicht meistens aus um die LEDs schön leuchten zu lassen, vor allem bei low-current und ultrahellen LEDs&lt;br /&gt;
* Wenn doch 20 mA pro LED gebraucht werden, kann man die folgende Trickschaltung anwenden.&lt;br /&gt;
&lt;br /&gt;
[[bild:8x20mA_LED_mit_74xx595.png]]&lt;br /&gt;
&lt;br /&gt;
Der Trick besteht darin, dass 4 LEDs ihren Strom über das Schieberegister von VCC beziehen (HIGH aktiv) während die anderen vier ihren Strom über GND leiten (LOW aktiv). Damit bleiben ganz offiziell für jede LED 70/4 = 17,5mA. Um die Handhabung in der Software zu vereinfachen muss nur vor der Ausgabe der Daten das jeweilige Byte mit 0x0F XOR verknüpft werden, bevor es in das Schieberegister getaktet wird. Dadurch werden die LOW-aktiven LEDs richtig angesteuert und die Datenhandhabung in der Software muss nur mit HIGH-aktiven rechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Achtung!&amp;lt;/B&amp;gt; Die Widerstände sind auf blaue LEDs mit 3,3V Flusspannung ausgelegt. Bei roten, gelben und grünen [[LED]]s ist die Flusspannung geringer und dementsprechend muss der Vorwiderstand grösser sein.&lt;br /&gt;
&lt;br /&gt;
Ausserdem wird der G Eingang verwendet, um die Helligkeit aller LEDs per [[PWM]] zu steuern. Beachtet werden muss, dass die PWM im invertierten Modus generiert werden muss, da der Eingang G LOW aktiv ist.&lt;br /&gt;
&lt;br /&gt;
== Porterweiterung für Eingänge ==&lt;br /&gt;
&lt;br /&gt;
Ein naher Verwandter des 75xx595 ist der 74xx165, er ist quasi das Gegenstück. Hierbei handet es sich um ein &#039;&#039;8-bit parallel-in/serial-out shift register&#039;&#039;. Auf deutsch ein 8 Bit Schieberegister mit parallelem Eingang und seriellem Ausgang. Damit kann man ein grosse Anzahl Eingänge sehr einfach und preiswert zu seinem Mikrocontroller hinzufügen.&lt;br /&gt;
&lt;br /&gt;
=== Aufbau ===&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-1.png]]&lt;br /&gt;
&lt;br /&gt;
Der Aufbau ist sehr ähnlich zum 74xx595. Allerdings gibt es kein Register zum Zwischenspeichern. Das ist auch gar nicht nötig, da der IC ja einen parallelen Eingang hat. Der muss nicht zwischengespeichert werden. Es gibt hier also wirklich nur das Schieberegister. Dieses wird über den Eingang PL mit den parallelen Daten geladen. Dann können die Daten seriell mit Takten an CLK aus dem Ausgang Q7 geschoben werden.&lt;br /&gt;
 &lt;br /&gt;
=== Funktionsweise ===&lt;br /&gt;
&lt;br /&gt;
DS ist der serielle Dateneingang, welcher im Falle von kaskadierten Schieberegistern mit dem Ausgang des vorhergehenden ICs verbunden wird.&lt;br /&gt;
&lt;br /&gt;
D0..D7 sind die parallelen Dateneingänge.&lt;br /&gt;
&lt;br /&gt;
Mittels des Eingangs PL (&#039;&#039;&#039;P&#039;&#039;&#039;arallel &#039;&#039;&#039;L&#039;&#039;&#039;oad) werden die Daten vom parallelen Eingang in das Schieberegister übernommen, wenn dieses Signal LOW ist. Hier muss man aber ein klein wenig aufpassen. Auf grund der Schaltungsstruktur ist der Eingang PL mit dem Takt CLK verknüpft (obwohl es dafür keinen logischen Grund gibt :-0). Damit es nicht zu unerwünschten Fehlschaltungen kommt, muss der Takt CLK während des Ladens auf HIGH liegen. Wird PL wieder auf HIGH gesetzt, sind die Daten geladen. Das erste Bit liegt direkt am Ausgang Q7 and. Die restlichen Bits können nach und nach durch das Register geschoben werden.&lt;br /&gt;
&lt;br /&gt;
Der Eingang CE (&#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;E&#039;&#039;&#039;nable) steuert, ob das Schieberegister auf den Takt CLK reagieren soll oder nicht. Ist CE gleich LOW werden alle Takte an CLK ignoriert. Bei HIGH werden mit jeder positiven Flanke die Daten um eine Stufe weiter geschoben.&lt;br /&gt;
&lt;br /&gt;
Wird am Eingang CLK eine LOW-HIGH Flanke angelegt und ist dabei CE auf LOW, dann werden die Daten im Schieberegister um eine Position weiter geschoben: DS-&amp;gt;Q0, Q0-&amp;gt;Q1, Q1-&amp;gt;Q2, Q2-&amp;gt;Q3, Q3-&amp;gt;Q4, Q4-&amp;gt;Q5, Q5-&amp;gt;Q6, Q6-&amp;gt;Q7. Q0..Q6 sind interne Signale, siehe [http://nxp.com/ Datenblatt].&lt;br /&gt;
&lt;br /&gt;
Q7 ist der Ausgang des Schieberegisters. Dort Werden Takt für Takt die Daten ausgeggeben. Hier wird normalerweise der Eingang des Mikrocontrollers oder der Eingang des nächsten Schieberegisters angeschlossen.&lt;br /&gt;
&lt;br /&gt;
Q7\ ist der invertierte Ausgang des Schieberegisters. Er wird meist nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Schaltung ===&lt;br /&gt;
&lt;br /&gt;
Um nun beispielsweise zwei Schieberegister zu kaskadieren um 16 Eingangspins zu erhalten sollte man folgende Verschaltung vornehmen. Beachten sollte man dabei, dass&lt;br /&gt;
&lt;br /&gt;
* der serielle Eingang DS des ersten Schieberegisters (hier IC1) auf einen festen Pegel gelegt wird (LOW oder HIGH).&lt;br /&gt;
* der serielle Datenausgang bei der Benutzung des SPI-Moduls an MISO und nicht an MOSI angeschlossen wird.&lt;br /&gt;
&lt;br /&gt;
[[bild:74xx165-2.png]]&lt;br /&gt;
&lt;br /&gt;
Nachfolgend werden zwei Beispiele gezeigt, welche die Ansteuerung nach bekanntem Muster übernehemn. Nur dass hier eben Daten gelesen anstatt geschrieben werden. Zu beachten ist, dass hier ein anderer Modus der SPI-Ansteuerung verwendet werden muss, weil der Baustein das nötig macht. Das muss beachtet werden, wenn auch Schieberegister für Ausgänge verwendet werden. Dabei muss jeweils vor dem Zugriff auf die Ein- oder Ausgangsregister der Modus des Taktes (CPOL) umgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per Software ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per Software&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen, frei wählbar&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ SCHIEBE_PIN  = PINB&lt;br /&gt;
.equ CLK          = PB3&lt;br /&gt;
.equ PL           = PB1&lt;br /&gt;
.equ DIN          = PB2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:		.byte 2				; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
	ldi		temp1, LOW(RAMEND)	; Stackpointer initialisieren&lt;br /&gt;
    out		SPL, temp1&lt;br /&gt;
    ldi		temp1, HIGH(RAMEND)&lt;br /&gt;
    out 	SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi   	temp1, (1&amp;lt;&amp;lt;clk) | (1&amp;lt;&amp;lt;pl)&lt;br /&gt;
    out   	SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
	sbi		schiebe_port, clk	; Takt im Ruhezustand immer auf 1&lt;br /&gt;
								; komische Schaltung im 74xx165&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi		ZL,low(Daten)&lt;br /&gt;
    ldi		ZH,high(Daten)&lt;br /&gt;
	ldi		temp1,2&lt;br /&gt;
    rcall	schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  	loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push	temp2				; Register sichern&lt;br /&gt;
	push	temp3&lt;br /&gt;
&lt;br /&gt;
	cbi		schiebe_port, pl	; Daten parallel laden&lt;br /&gt;
	sbi		schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
&lt;br /&gt;
	ldi		temp3, 8			; Bitzähler&lt;br /&gt;
schiebe_eingang_bit_schleife:&lt;br /&gt;
	lsl		temp2				; Daten weiterschieben&lt;br /&gt;
&lt;br /&gt;
; das IO Bit Din in das niederwerigste Bit von temp2 kopieren&lt;br /&gt;
&lt;br /&gt;
 	sbic	schiebe_pin, din&lt;br /&gt;
	ori		temp2,1				; Bit setzen&lt;br /&gt;
	sbis	schiebe_pin, din&lt;br /&gt;
	andi	temp2, ~1			; Bit löschen&lt;br /&gt;
&lt;br /&gt;
    cbi   	SCHIEBE_PORT, CLK   ; Taktausgang auf 0&lt;br /&gt;
    sbi   	SCHIEBE_PORT, CLK   ; und wieder zurück auf 1, dabei Daten schieben &lt;br /&gt;
&lt;br /&gt;
    dec   	temp3             	; Bitzähler um eins verringern&lt;br /&gt;
    brne  	schiebe_eingang_bit_schleife ;wenn noch keine 8 Bits ausgegeben, nochmal&lt;br /&gt;
&lt;br /&gt;
	st		z+,temp2			; Datenbyte speichern&lt;br /&gt;
	dec		temp1				; Anzahl Bytes um eins verringern&lt;br /&gt;
	brne	schiebe_eingang_byte_schleife	; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
	pop		temp3&lt;br /&gt;
    pop		temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ansteuerung per SPI-Modul ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Porterweiterung für Eingänge mit Schieberegister 74xx165&lt;br /&gt;
; Ansteuerung per SPI-Modul&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
&lt;br /&gt;
; Pins anpassen&lt;br /&gt;
; diese müssen mit den SPI-Pins des AVR Typs übereinstimmen!&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ PL           = PB2     	; SS&lt;br /&gt;
.equ CLK          = PB5     	; SCK&lt;br /&gt;
.equ DIN          = PB4     	; MISO&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Daten:		.byte 2				; Speicherplatz für Eingangsdaten&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
.cseg&lt;br /&gt;
	ldi		temp1, LOW(RAMEND)	; Stackpointer initialisieren&lt;br /&gt;
    out		SPL, temp1&lt;br /&gt;
    ldi		temp1, HIGH(RAMEND)&lt;br /&gt;
    out 	SPH, temp1&lt;br /&gt;
&lt;br /&gt;
; CLK und PL als Ausgänge schalten&lt;br /&gt;
&lt;br /&gt;
    ldi   	temp1,(1&amp;lt;&amp;lt;CLK) | (1&amp;lt;&amp;lt;PL)&lt;br /&gt;
    out   	SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   	temp1, 0b01011000&lt;br /&gt;
    out   	SPCR, temp1       	; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 1, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   	temp1,1&lt;br /&gt;
    out   	SPSR,temp1        	; double speed aktivieren&lt;br /&gt;
    out   	SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; Zwei Bytes einlesen&lt;br /&gt;
&lt;br /&gt;
    ldi		ZL,low(Daten)&lt;br /&gt;
    ldi		ZH,high(Daten)&lt;br /&gt;
	ldi		temp1,2&lt;br /&gt;
    rcall	schiebe_eingang&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  	loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes seriell einlesen&lt;br /&gt;
;&lt;br /&gt;
; temp1 : N, Anzahl der Bytes&lt;br /&gt;
; Z     : Zeiger auf einen Datenbereich im SRAM&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
schiebe_eingang:&lt;br /&gt;
    push	temp2				; Register sichern&lt;br /&gt;
&lt;br /&gt;
	; CLK ist im Ruhezustand schon auf HIGH, CPOL=1&lt;br /&gt;
&lt;br /&gt;
	cbi		schiebe_port, pl	; Daten parallel laden&lt;br /&gt;
	sbi		schiebe_port, pl&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_1:&lt;br /&gt;
	sbis    SPSR,7          	; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    schiebe_eingang_1&lt;br /&gt;
&lt;br /&gt;
schiebe_eingang_byte_schleife:&lt;br /&gt;
    out     SPDR,temp1      	; beliebige Daten ins SPI Modul schreiben&lt;br /&gt;
								; um die Übertragung zu starten&lt;br /&gt;
schiebe_eingang_2:&lt;br /&gt;
	sbis    SPSR,7          	; auf das Ende der Übertragung warten&lt;br /&gt;
    rjmp    schiebe_eingang_2&lt;br /&gt;
&lt;br /&gt;
	in		temp2, spdr			; Daten lesen&lt;br /&gt;
	st		z+,temp2			; Datenbyte speichern&lt;br /&gt;
	dec		temp1				; Anzahl Bytes um eins verringern&lt;br /&gt;
	brne	schiebe_eingang_byte_schleife	; wenn noch mehr Bytes zu lesen sind&lt;br /&gt;
&lt;br /&gt;
    pop		temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
AVR Studio 4.12 (Build 498) hat Probleme bei der korrekten Simulation des SPI-Moduls.&lt;br /&gt;
&lt;br /&gt;
* Der Double-Speed Modus funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
* Das Bit SPIF im Register SPSR, welches laut Dokumentation nur lesbar ist, ist im Simulator auch schreibbar! Das kann zu Verwirrung und Fehlern in der Simulation führen.&lt;br /&gt;
&lt;br /&gt;
Hardwareprobleme&lt;br /&gt;
&lt;br /&gt;
* Wenn das SPI-Modul aktiviert wird, wird &#039;&#039;&#039;NICHT&#039;&#039;&#039; automatisch SPIF gesetzt, es bleibt auf Null. Damit würde die erste Abfrage in &#039;&#039;Schiebe_alles&#039;&#039; in einer Endlosschleife hängen bleiben. Deshalb muss nach der Initialisierung des SPI-Moduls ein Dummy Byte gesendet werden, damit am Ende der Übertragung SPIF gesetzt wird&lt;br /&gt;
&lt;br /&gt;
* Da das SPI-Modul in Senderichtung nur einfach gepuffert ist, ist es nicht möglich absolut lückenlos Daten zu senden, auch wenn man mit &#039;&#039;&#039;nop&#039;&#039;&#039; eine feste minimale Zeit zwischen zwei Bytes warten würde. Zwischen zwei Bytes muss immer eine Pause von mind. 2 Systemtakten eingehalten werden.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=PWM|&lt;br /&gt;
zurücklink=AVR-Tutorial: PWM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=SRAM|&lt;br /&gt;
vorlink=AVR-Tutorial: SRAM}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:74xx165-2.png&amp;diff=25436</id>
		<title>Datei:74xx165-2.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:74xx165-2.png&amp;diff=25436"/>
		<updated>2008-01-08T22:37:28Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:74xx165-1.png&amp;diff=25435</id>
		<title>Datei:74xx165-1.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:74xx165-1.png&amp;diff=25435"/>
		<updated>2008-01-08T22:37:08Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25433</id>
		<title>AVR-Tutorial: Schieberegister</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Schieberegister&amp;diff=25433"/>
		<updated>2008-01-08T18:44:57Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Formatierung verbessert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ab und an stellt sich folgendes Problem: Man würde wesentlich mehr Ausgangsleitungen zu Steuerzwecken benötigen, als der µC zur Verfügung stellt. Ein möglicher Ausweg ist eine Porterweiterung mit einem Schieberegister. Ein beliebter Schieberegisterbaustein ist beispielsweise der 74xxx595 (74HC595, 74HCT595, 74SN595, 74AC595...).&lt;br /&gt;
&lt;br /&gt;
==Der Baustein 74xxx595==&lt;br /&gt;
Ein 74xxx595 ist ein &#039;&#039;8-Bit 3-state Serial-in/Serial-out or Parallel-Out Schieberegister mit einem Ausgaberegister und einem asynchronen Reset&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Hinter dieser kompliziert anmutenden Beschreibung verbirgt sich eine einfache Funktionalität: Das Schieberegister besteht aus zwei Funktionseinheiten: Dem eigentlichen Schieberegister und dem Ausgangsregister. In das Schieberegister können die Daten seriell hineingetaktet werden und durch ein bestimmtes Signal werden die Daten der Schiebestufe in das Ausgangsregister übernommen und können von dort auf die Ausgangspins geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Im Einzelnen bedeuten die Begriffe:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Begriff || Erklärung&lt;br /&gt;
|-&lt;br /&gt;
|8-Bit   || Acht Ausgangs[[Bit|bit]]s&lt;br /&gt;
|-&lt;br /&gt;
|3-state || Die acht Registerausgänge können drei Zustände, Low, High und High-Impedanz annehmen. Siehe [[Ausgangsstufen Logik-ICs]]&lt;br /&gt;
|-&lt;br /&gt;
|Serial-in || Serieller Eingang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Serial-out || Serieller Ausgang des Schieberegisters&lt;br /&gt;
|-&lt;br /&gt;
|Parallel-Out || Parallele Ausgänge des Ausgangsregisters&lt;br /&gt;
|-&lt;br /&gt;
|Schieberegister || Serielle Daten werden durch den Baustein durchgeschoben&lt;br /&gt;
|-&lt;br /&gt;
|Ausgangsregister || Ein Speicher, welcher die Daten des Schieberegisters zwischenspeichern kann.&amp;lt;BR&amp;gt;Dieses besteht aus acht [[FlipFlop]]s.&lt;br /&gt;
|-&lt;br /&gt;
|Asynchroner Reset || Die Daten im Schieberegister können asynchron zum zurückgesetzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Pinbelegung des Bausteins===&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-1.png]]&lt;br /&gt;
&lt;br /&gt;
Hinweis: Die Benennung der Pins in den Datenblättern verschiedener Hersteller unterscheidet sich zum Teil. Die Funktionen der Pins sind jedoch gleich.&lt;br /&gt;
&lt;br /&gt;
{| cellspacing=”0” border=&amp;quot;1&amp;quot; &lt;br /&gt;
|+ &#039;&#039;&#039;Beispiele von Hersteller-Pinbenennungen&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
! DIL Pin-Nummer || Funktion || Dieses Tutorial || Motorola / ON Semi || Philips         || Fairchild     || SGS|| Texas Instruments&lt;br /&gt;
|-&lt;br /&gt;
|  1 || Ausgang B           || QB || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;1&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  || QB|| Q&amp;lt;sub&amp;gt;B&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  2 || Ausgang C           || QC || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  || QC|| Q&amp;lt;sub&amp;gt;C&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  3 || Ausgang D           || QD || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;3&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  || QD|| Q&amp;lt;sub&amp;gt;D&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  4 || Ausgang E           || QE || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;4&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  || QE|| Q&amp;lt;sub&amp;gt;E&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  5 || Ausgang F           || QF || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;5&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  || QF|| Q&amp;lt;sub&amp;gt;F&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  6 || Ausgang G           || QG || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;6&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  || QG|| Q&amp;lt;sub&amp;gt;G&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  7 || Ausgang H           || QH || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  || QH|| Q&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
|  8 || Masse, 0 V || [nicht dargestellt] || GND            || GND             || GND             || GND|| GND&lt;br /&gt;
|-&lt;br /&gt;
|  9 || Serieller Ausgang || QH* || SQ&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt; || Q&amp;lt;sub&amp;gt;7&amp;lt;/sub&amp;gt;´  ||Q&#039;&amp;lt;sub&amp;gt;H&amp;lt;/sub&amp;gt;  ||  QH´||Q&amp;lt;sub&amp;gt;H&#039;&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 10 || Reset für Schieberegister             || SCL || RESET          || /MR             || /SCLR        || /SCLR || /SRCLR&lt;br /&gt;
|-&lt;br /&gt;
| 11 || Schiebetakt        || SCK || SHIFT CLOCK    || SH&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || SCK          || SCK || SRCLK&lt;br /&gt;
|-&lt;br /&gt;
| 12 || Speichertakt        || RCK || LATCH CLOCK    || ST&amp;lt;sub&amp;gt;CP&amp;lt;/sub&amp;gt; || RCK          || RCK || RCLK&lt;br /&gt;
|-&lt;br /&gt;
| 13 || Ausgangssteuerung      || G || OUTPUT ENABLE  || /OE             || /G           || /G || /OE&lt;br /&gt;
|-&lt;br /&gt;
| 14 || Serieller Dateneingang  || SER || A              || D&amp;lt;sub&amp;gt;S&amp;lt;/sub&amp;gt;   || SER          || SI || SER&lt;br /&gt;
|-&lt;br /&gt;
| 15 || Ausgang A           || QA || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || Q&amp;lt;sub&amp;gt;0&amp;lt;/sub&amp;gt;   || Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  || QA|| Q&amp;lt;sub&amp;gt;A&amp;lt;/sub&amp;gt;  &lt;br /&gt;
|-&lt;br /&gt;
| 16 || Betriebsspannung || [nicht dargestellt] || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt; || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;  || V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;|| V&amp;lt;sub&amp;gt;CC&amp;lt;/sub&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Prinzipskizze des Bausteins===&lt;br /&gt;
&lt;br /&gt;
Der Baustein besteht aus zwei Einheiten:&lt;br /&gt;
* dem Schieberegister&lt;br /&gt;
* dem Ausgangsregister&lt;br /&gt;
&lt;br /&gt;
Im Schieberegister werden die einzelnen Bits durchgeschoben. Mit jeder positiven Taktflanke(LOW -&amp;gt; HIGH) an &#039;&#039;&#039;SCK&#039;&#039;&#039; wird eine Schiebeoperation durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Das Ausgangsregister hat die Aufgabe die Ausgangspins des Bausteins anzusteuern. Durch dieses Ausgangsregister ist es möglich, die Schiebeoperationen im Hintergrund durchzuführen, ohne dass IC Pins ihren Wert ändern. Erst wenn die Schiebeoperation abgeschlossen ist, wird der aktuelle Zustand der Schieberegisterkette durch einen Puls an &#039;&#039;&#039;RCK&#039;&#039;&#039; in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
===Funktionsweise===&lt;br /&gt;
&lt;br /&gt;
Am Eingang &#039;&#039;&#039;SER&#039;&#039;&#039; (Pin 14) wird das gewünschte nächste Datum (0 oder 1) angelegt. Durch einen positiven Puls an &#039;&#039;&#039;SCK&#039;&#039;&#039; (Pin 1) wird der momentan an &#039;&#039;&#039;SER&#039;&#039;&#039; anliegende Wert als neuer Wert für Bit 0, das unterste Bit des Schieberegisters, übernommen. Gleichzeitig werden alle anderen Bits im Schieberegister um eine Stelle verschoben: Das Bit 6 wird ins Bit 7 übernommen, Bit 5 ins Bit 6, Bit 4 ins Bit 5, etc. sodass das Bit 0 zur Aufnahme des &#039;&#039;&#039;SER&#039;&#039;&#039; Bits frei wird.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-2.png]]&lt;br /&gt;
&lt;br /&gt;
Eine Sonderstellung nimmt das ursprüngliche Bit 7 ein. Dieses Bit steht direkt auch am Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; (Pin 9) zur Verfügung. Dadurch ist es möglich an ein Schieberegister einen weiteren Baustein 74xxx595 anzuschliessen und so beliebig viele Schieberegister hintereinander zu schalten (kaskadieren). Auf diese Art lassen sich Schieberegister mit beliebig vielen Stufen aufbauen.&lt;br /&gt;
&lt;br /&gt;
Wurde das Schieberegister mit den Daten gefüllt, so wird mit einem LOW-HIGH Puls am Pin 12, &#039;&#039;&#039;RCK&#039;&#039;&#039; der Inhalt des Schieberegisters in das Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:74xx595-3.png]]&lt;br /&gt;
&lt;br /&gt;
Mit dem Eingang &#039;&#039;&#039;G&#039;&#039;&#039; (Pin 13) kann das Ausgangsregister freigegeben werden. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 0, so führen die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; entsprechende Pegel. Liegt &#039;&#039;&#039;G&#039;&#039;&#039; auf 1, so schalten die Ausgänge &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; auf [[Ausgangsstufen Logik-ICs |Tristate]]. D.h. sie treiben aktiv weder LOW oder HIGH, sondern sind hochohmig wie ein Eingänge und nehmen jeden Pegel an, der ihnen von aussen aufgezwungen wird.&lt;br /&gt;
&lt;br /&gt;
Bleibt nur noch der Eingang &#039;&#039;&#039;SCL&#039;&#039;&#039;(Pin 13). Mit ihm kann das Schieberegister im Baustein gelöscht, also auf eine definierte 0, gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
==Programmierung des 74xxx595==&lt;br /&gt;
&lt;br /&gt;
Die Programmierung eines 74xxx595 Schieberegisters gestaltet sich sehr einfach. Im Grunde gibt es 2 Möglichkeiten:&lt;br /&gt;
* Mittels [[SPI]] kann der [[AVR]] das Schieberegister direkt und autark ansteuern. Das ist sehr schnell und verbraucht nur wenig CPU-Leistung&lt;br /&gt;
* Sind die entsprechenden SPI-Pins am AVR nicht frei, so ist auch eine softwaremässige Ansteuerung des Schieberegisters mit einfachen Mitteln durchführbar.&lt;br /&gt;
&lt;br /&gt;
=== Direkte Ansteuerung per Software===&lt;br /&gt;
&lt;br /&gt;
Für eine komplette Softwarelösung kann das Schieberegister an jede beliebige Port-Pin Kombination angeschlossen werden. Wir wählen die Pins &#039;&#039;&#039;PB0&#039;&#039;&#039;, &#039;&#039;&#039;PB1&#039;&#039;&#039;, &#039;&#039;&#039;PB2&#039;&#039;&#039; und &#039;&#039;&#039;PB3&#039;&#039;&#039; um dort die Schieberegisteranschlüsse &#039;&#039;&#039;SER&#039;&#039;&#039;, &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;SCL&#039;&#039;&#039; und &#039;&#039;&#039;RCK&#039;&#039;&#039; anzuschliessen.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Programmierung gestaltet sich dann nach folgendem Schema: Die 8 Bits eines Bytes werden nacheinander an den Ausgang &#039;&#039;&#039;PB0&#039;&#039;&#039; (&#039;&#039;&#039;SER&#039;&#039;&#039;) ausgegeben. Durch Generierung eines Pulses 0-1-0 an Pin &#039;&#039;&#039;PB1&#039;&#039;&#039; (&#039;&#039;&#039;SCK&#039;&#039;&#039;) übernimmt das Schieberegister nacheinander die einzelnen Bits. Dabei ist zu beachten, dass die Ausgabe mit dem höherwertigen Bit beginnen muss, denn dieses Bit wandert ja am weitesten zur Stelle &#039;&#039;&#039;QH&#039;&#039;&#039;. Sind alle 8 Bits ausgegeben, so wird durch einen weiteren 0-1-0 Impuls am Pin &#039;&#039;&#039;PB3&#039;&#039;&#039; (&#039;&#039;&#039;RCK&#039;&#039;&#039;) der Inhalt der Schieberegisterbits 0 bis 7 in die Ausgaberegister &#039;&#039;&#039;QA&#039;&#039;&#039; bis &#039;&#039;&#039;QH&#039;&#039;&#039; übernommen. Dadurch, dass am Schieberegister der Eingang &#039;&#039;&#039;G&#039;&#039;&#039; konstant auf 0-Pegel gehalten wird, erscheint dann auch die Ausgabe sofort an den entsprechenden Pins und kann zb. mit LEDs (low-current LEDs + Vorwiderstand verwenden) sichtbar gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Der Schieberegistereingang &#039;&#039;&#039;SCL&#039;&#039;&#039; wird auf einer 1 gehalten. Würde er&lt;br /&gt;
auf 0 gehen, so würde die Schieberegisterkette gelöscht. Möchte man einen weiteren Prozessorpin einsparen, so kann man diesen Pin auch generell auf Vcc legen. Das Schieberegister könnte man in so einem Fall durch Einschreiben von 0x00 immer noch löschen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = 3&lt;br /&gt;
.equ SCK          = 1&lt;br /&gt;
.equ SCL          = 2&lt;br /&gt;
.equ SIN          = 0&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Die Port Pins auf Ausgang konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, (1&amp;lt;&amp;lt;RCK) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;SCL) | (1&amp;lt;&amp;lt;SIN)&lt;br /&gt;
    out   SCHIEBE_DDR, temp1&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; die Clear Leitung am Schieberegister auf 1 stellen&lt;br /&gt;
;&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCL&lt;br /&gt;
&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe&lt;br /&gt;
    rcall SchiebeOut&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Ausgabe im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
Schiebe:&lt;br /&gt;
    push  temp2&lt;br /&gt;
    ldi   temp2, 8             ; 8 Bits müssen ausgegeben werden&lt;br /&gt;
&lt;br /&gt;
Schiebe_1:&lt;br /&gt;
     ;&lt;br /&gt;
     ; jeweils das höchstwertige Bit aus temp1 ins Carry-Flag schieben&lt;br /&gt;
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend&lt;br /&gt;
     ; gesetzt oder gelöscht&lt;br /&gt;
     ;&lt;br /&gt;
    rol  temp1                 ; MSB -&amp;gt; Carry&lt;br /&gt;
    brcs Schiebe_One           ; Carry gesetzt? -&amp;gt; weiter bei Schiebe_One&lt;br /&gt;
    cbi  SCHIEBE_PORT, SIN     ; Eine 0 ausgeben&lt;br /&gt;
    rjmp Schiebe_Clock         ; und Sprung zur Clock Puls Generierung&lt;br /&gt;
Schiebe_One:&lt;br /&gt;
    sbi  SCHIEBE_PORT, SIN     ; Eine 1 ausgeben&lt;br /&gt;
&lt;br /&gt;
     ;&lt;br /&gt;
     ; einen Impuls an SCK zur Übernahme des Bits nachschieben&lt;br /&gt;
     ;&lt;br /&gt;
Schiebe_Clock:&lt;br /&gt;
    sbi   SCHIEBE_PORT, SCK    ; Clock-Ausgang auf 1 ...&lt;br /&gt;
    cbi   SCHIEBE_PORT, SCK    ; und wieder zurück auf 0&lt;br /&gt;
&lt;br /&gt;
    dec   temp2                ; Anzahl der ausgegebenen Bits runterzählen&lt;br /&gt;
    brne  Schiebe_1            ; Wenn noch keine 8 Bits ausgegeben -&amp;gt; Schleife bilden&lt;br /&gt;
&lt;br /&gt;
    pop   temp2&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Ansteuerung mittels SPI===&lt;br /&gt;
&lt;br /&gt;
Noch schneller geht die Ansteuerung des Schieberegisters mittels [[Serial_Peripheral_Interface | SPI]]-Modul, welches in fast allen AVRs vorhanden ist. Hier wird der Pin &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt, da das praktisch keinen Sinn hat. Er muss also fest auf VCC gelegt werden. Die Pins für &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;SIN&#039;&#039;&#039; sind duch den jeweiligen AVR fest vorgegeben. &#039;&#039;&#039;SCK&#039;&#039;&#039; vom 74xxx595 wird mit &#039;&#039;&#039;SCK&#039;&#039;&#039; vom AVR verbunden sowie &#039;&#039;&#039;SIN&#039;&#039;&#039; mit &#039;&#039;&#039;MOSI&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;O&#039;&#039;&#039;ut, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;I&#039;&#039;&#039;n). &#039;&#039;&#039;MISO&#039;&#039;&#039; (&#039;&#039;&#039;M&#039;&#039;&#039;aster &#039;&#039;&#039;I&#039;&#039;&#039;n, &#039;&#039;&#039;S&#039;&#039;&#039;lave &#039;&#039;&#039;O&#039;&#039;&#039;ut) ist hier ungenutzt. Es kann NICHT als &#039;&#039;&#039;RCK&#039;&#039;&#039; verwendet werden, da es im SPI-Master Modus immer ein Eingang ist! Es kann aber als allgemeiner Eingang verwendet werden. Der AVR-Pin &#039;&#039;&#039;SS&#039;&#039;&#039; wird sinnvollerweise als &#039;&#039;&#039;RCK&#039;&#039;&#039; benutzt, da er sowieso als Ausgang geschaltet werden &#039;&#039;&#039;muss&#039;&#039;&#039;, sonst gibt es böse Überaschungen (siehe Datenblatt &amp;quot;SS Pin Functionality&amp;quot;). Je nach Bedarf kann man die Taktrate des SPI-Moduls zwischen 1/2 ... 1/128 des CPU-Taktes wählen. Es spricht kaum etwas dagegen mit maximaler Geschwindigkeit zu arbeiten. Die AVRs können zur Zeit mit maximal 20 MHz getaktet werden, d.h. es sind maximal 10 MHz SPI-Takt möglich. Das ist für ein 74xxx595 kein Problem. Die Übertragung von 8 Bit dauert dann gerade mal 800ns!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
    ldi   temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out   SPL, temp1&lt;br /&gt;
    ldi   temp1, HIGH(RAMEND)&lt;br /&gt;
    out   SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in    temp1,SCHIEBE_DDR&lt;br /&gt;
    ori   temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out   SCHIEBE_DDR,temp1     &lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b01010000&lt;br /&gt;
    out   SPCR, temp1           ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi   temp1,1&lt;br /&gt;
    out   SPSR,temp1            ; double speed aktivieren&lt;br /&gt;
    out   SPDR,temp1            ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
;&lt;br /&gt;
; Ein Datenbyte ausgeben&lt;br /&gt;
;&lt;br /&gt;
    ldi   temp1, 0b10101010&lt;br /&gt;
    rcall Schiebe               ; Daten schieben&lt;br /&gt;
    rcall SchiebeOut            ; Daten in Ausgangsregister übernehmen&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    rjmp  loop&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Die Daten im Schieberegister in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen&lt;br /&gt;
;&lt;br /&gt;
SchiebeOut:&lt;br /&gt;
    sbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    cbi   SCHIEBE_PORT, RCK&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; 8 Bits aus temp1 an das Schieberegister ausgeben&lt;br /&gt;
;&lt;br /&gt;
Schiebe:&lt;br /&gt;
    sbis    SPSR,7          ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe&lt;br /&gt;
    out     SPDR,temp1      ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Kaskadieren von Schieberegistern==&lt;br /&gt;
&lt;br /&gt;
Um ein Schieberegister anzuschließen genügen also im einfachsten Fall 4 freie Prozessorpins (3 wenn &#039;&#039;&#039;SCL&#039;&#039;&#039; nicht benutzt wird) um weitere 8 Ausgangsleitungen zu bekommen. Genügen diese 8 Leitungen nicht, so kann ohne Probleme ein weiteres Schieberegister an das bereits Vorhandene angeschlossen werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Mega8-595-2.gif]]&lt;br /&gt;
&lt;br /&gt;
Das nächste Schieberegister wird mit seinem Dateneingang &#039;&#039;&#039;SER&#039;&#039;&#039; einfach an den dafür vorgesehenen Ausgang &#039;&#039;&#039;QH*&#039;&#039;&#039; des vorhergehenden Schieberegisters angeschlossen. Die Steuerleitungen &#039;&#039;&#039;SCK&#039;&#039;&#039;, &#039;&#039;&#039;RCK&#039;&#039;&#039; und &#039;&#039;&#039;SCL&#039;&#039;&#039; werden parallel zu den bereits vorhandenen geschaltet. Konzeptionell erhält man dadurch ein Schieberegister mit einer Breite von 16 Bit. Werden weiter Bausteine in derselben Manier angeschlossen, so erhöht sich die Anzahl der zur Verfügung stehenden Ausgabeleitungen mit jedem Baustein um 8 ohne dass sich die Anzahl der am Prozessor notwendigen Ausgabepins erhöhen würde. Um diese weiteren Register zu nutzen, muss man in der reinen Softwarelösung nur mehrfach die Funktion &#039;&#039;&#039;Schiebe&#039;&#039;&#039; aufrufen, um alle Daten auszugeben. Am Ende werden dann mit &#039;&#039;&#039;SchiebeOut&#039;&#039;&#039; die Daten in die Ausgangsregister übernommen.&lt;br /&gt;
&lt;br /&gt;
Bei der SPI Lösung werden ebenfalls ganz einfach mehrere Bytes über SPI ausgegeben, ehe dann mittels &#039;&#039;&#039;RCK&#039;&#039;&#039; die in die Schieberegisterkette eingetakteten Bits in das Ausgangsregister übernommen werden.&lt;br /&gt;
Um das Ganze ein wenig zu vereinfachen, soll hier eine Funktion zur Ansteuerung mehrerer kaskadierter Schieberegister über das SPI-Modul gezeigt werden. Dabei wird die Ausgabe mehrerer Bytes über eine Schleife realisiert, mehrfache Aufrufe der Funktion sind damit nicht nötig. Statt dessen übergibt man einen Zeiger auf einen Datenblock im RAM sowie die Anzahl der zu übertragenden Bytes. Ausserdem wird die Datenübernahme durch &#039;&#039;&#039;RCK&#039;&#039;&#039; standardkonform integriert. Denn bei nahezu allen ICs mit SPI wird ein sog. CS-Pin verwendet (&#039;&#039;&#039;C&#039;&#039;&#039;hip &#039;&#039;&#039;S&#039;&#039;&#039;elect) Dieser Pin ist meist LOW aktiv, d.h. wenn er HIGH ist, ignoriert der IC alle Signale an &#039;&#039;&#039;SCK&#039;&#039;&#039; und &#039;&#039;&#039;MOSI&#039;&#039;&#039; und gibt keine Daten an MISO aus. Ist er LOW, dann ist der IC aktiv und funktioniert normal. Bei der steigenden Flanke an &#039;&#039;&#039;CS&#039;&#039;&#039; werden die Daten ins Ausgangsregister übernommen. Die Fuktion ist sehr schnell, da die Zeit während der die Übertragung eines Bytes läuft, dazu genutzt wird, den Schleifenzähler zu verringern und zu prüfen sowie neue Sendedaten zu laden. Zwischen den einzelnen Bytes gibt es somit nur eine Pause von max. 6 Systemtakten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
&lt;br /&gt;
; Die Definitionen müssen an den jeweiligen AVR angepasst werden&lt;br /&gt;
&lt;br /&gt;
.equ SCHIEBE_DDR  = DDRB&lt;br /&gt;
.equ SCHIEBE_PORT = PORTB&lt;br /&gt;
.equ RCK          = PB2     ; SS&lt;br /&gt;
.equ SCK          = PB5     ; SCK&lt;br /&gt;
.equ SIN          = PB3     ; MOSI&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Datensegment im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.dseg&lt;br /&gt;
.org $60&lt;br /&gt;
Schiebedaten:       .byte 2&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; Programmsegment im FLASH&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
    ldi     temp1, LOW(RAMEND)     ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
;&lt;br /&gt;
; SCK, MOSI, SS als Ausgänge schalten&lt;br /&gt;
;&lt;br /&gt;
    in      temp1,SCHIEBE_DDR&lt;br /&gt;
    ori     temp1,(1&amp;lt;&amp;lt;SIN) | (1&amp;lt;&amp;lt;SCK) | (1&amp;lt;&amp;lt;RCK) &lt;br /&gt;
    out     SCHIEBE_DDR,temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; Slave select inaktiv&lt;br /&gt;
;&lt;br /&gt;
; SPI Modul konfigurieren&lt;br /&gt;
;&lt;br /&gt;
    ldi     temp1, 0b01010000&lt;br /&gt;
    out     SPCR, temp1         ; keine Interrupts, MSB first, Master&lt;br /&gt;
                                ; CPOL = 0, CPHA =0&lt;br /&gt;
                                ; SCK Takt = 1/2 XTAL&lt;br /&gt;
    ldi     r16,1&lt;br /&gt;
    out     SPSR,r16            ; Double Speed&lt;br /&gt;
    out     SPDR,temp1          ; Dummy Daten, um SPIF zu setzen&lt;br /&gt;
&lt;br /&gt;
; den Datenblock mit Daten füllen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1,$F0&lt;br /&gt;
    sts     Schiebedaten,temp1&lt;br /&gt;
    ldi     temp1,$55&lt;br /&gt;
    sts     Schiebedaten+1,temp1&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
; den Datenblock ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     r16,2&lt;br /&gt;
    ldi     zl,low(Schiebedaten)&lt;br /&gt;
    ldi     zh, high(Schiebedaten)&lt;br /&gt;
    rcall   Schiebe_alle                    ; Daten ausgeben&lt;br /&gt;
&lt;br /&gt;
    rjmp  loop                              ; nur zur Simulation&lt;br /&gt;
&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
;&lt;br /&gt;
; N Bytes an das Schieberegister ausgeben und in das Ausgaberegister übernehmen&lt;br /&gt;
;&lt;br /&gt;
; r16: Anzahl der Datenbytes&lt;br /&gt;
; Z: Zeiger auf Datenblock im RAM&lt;br /&gt;
;&lt;br /&gt;
;-----------------------------------------------------------------------------&lt;br /&gt;
Schiebe_alle:&lt;br /&gt;
    cbi     SCHIEBE_PORT, RCK   ; RCK LOW, SPI Standardverfahren&lt;br /&gt;
    push    r17&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_2:&lt;br /&gt;
    ld      r17,Z+&lt;br /&gt;
Schiebe_alle_3:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob eine alte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_3&lt;br /&gt;
    out     SPDR,r17            ; Daten ins SPI Modul schreiben, Übertragung beginnt automatisch&lt;br /&gt;
    dec     r16&lt;br /&gt;
    brne    Schiebe_alle_2&lt;br /&gt;
&lt;br /&gt;
Schiebe_alle_4:&lt;br /&gt;
    sbis    SPSR,SPIF           ; prüfe ob die letzte Übertragung beendet ist&lt;br /&gt;
    rjmp    Schiebe_alle_4&lt;br /&gt;
&lt;br /&gt;
    pop     r17&lt;br /&gt;
    sbi     SCHIEBE_PORT, RCK   ; RCK inaktiv, Datenübernahme&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Nachteil von Schieberegistern ist allerdings, dass sich die Zeit zum Setzten aller Ausgabeleitungen mit jedem weiteren Baustein immer weiter erhöht. Dies deshalb, da ja die einzelnen Bits im Gänsemarsch durch alle Bausteine geschleust werden müssen und für jeden einzelnen Schiebevorgang etwas Zeit notwendig ist. Ein Ausweg ist die Verwendung des SPI-Moduls, welches schneller arbeitet als die reine Softwarelösung. Ist noch mehr Geschwindigkeit gefragt, so sind mehr Port-Pins nötig. Kann ein kompletter Port mit 8 Pins für die Daten genutzt werden, sowie ein paar weitere Steuerleitungen, so können ein oder mehrere 74xxx573 eine Alternative sein, um jeweils ein vollständiges Byte auszugeben. Natürlich kann der 74xxx573 (oder ein ähnliches Schieberegister) auch mit dem 74xxx595 zusammen eingesetzt werden, beispielsweise in dem über das Schieberegister verschiedene 74xxx595 nacheinander aktiviert werden. Weitere Tips und Tricks dazu gibt es vielleicht in einem weiteren Tutorial...&lt;br /&gt;
&lt;br /&gt;
== Acht LEDs mit je 20mA pro Schieberegister ==&lt;br /&gt;
&lt;br /&gt;
Will man nun acht [[LED]]s mit dem Schieberegister ansteuern, kann man diese direkt über Vorwiderstände anschliessen. Doch ein genauer Blick ins Datenblatt verrät, dass der 74xx595 nur maximal 70mA über VCC bzw. GND ableiten kann. Und wenn man den IC nicht gnadenlos quälen, und damit die Lebensdauer und Zuverlässigkeit drastisch reduzieren will, gibt es nur zwei Auswege.&lt;br /&gt;
&lt;br /&gt;
* Den Strom pro LED auf 70/8 = 8,75mA begrenzen; Das reicht meistens aus um die LEDs schön leuchten zu lassen, vor allem bei low-current und ultrahellen LEDs&lt;br /&gt;
* Wenn doch 20 mA pro LED gebraucht werden, kann man die folgende Trickschaltung anwenden.&lt;br /&gt;
&lt;br /&gt;
[[bild:8x20mA_LED_mit_74xx595.png]]&lt;br /&gt;
&lt;br /&gt;
Der Trick besteht darin, dass 4 LEDs ihren Strom über das Schieberegister von VCC beziehen (HIGH aktiv) während die anderen vier ihren Strom über GND leiten (LOW aktiv). Damit bleiben ganz offiziell für jede LED 70/4 = 17,5mA. Um die Handhabung in der Software zu vereinfachen muss nur vor der Ausgabe der Daten das jeweilige Byte mit 0x0F XOR verknüpft werden, bevor es in das Schieberegister getaktet wird. Dadurch werden die LOW-aktiven LEDs richtig angesteuert und die Datenhandhabung in der Software muss nur mit HIGH-aktiven rechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Achtung!&amp;lt;/B&amp;gt; Die Widerstände sind auf blaue LEDs mit 3,3V Flusspannung ausgelegt. Bei roten, gelben und grünen [[LED]]s ist die Flusspannung geringer und dementsprechend muss der Vorwiderstand grösser sein.&lt;br /&gt;
&lt;br /&gt;
Ausserdem wird der G Eingang verwendet, um die Helligkeit aller LEDs per [[PWM]] zu steuern. Beachtet werden muss, dass die PWM im invertierten Modus generiert werden muss, da der Eingang G LOW aktiv ist.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
&lt;br /&gt;
AVR Studio 4.12 (Build 498) hat Probleme bei der korrekten Simulation des SPI-Moduls.&lt;br /&gt;
&lt;br /&gt;
* Der Double-Speed Modus funktioniert nicht.&lt;br /&gt;
&lt;br /&gt;
* Das Bit SPIF im Register SPSR, welches laut Dokumentation nur lesbar ist, ist im Simulator auch schreibbar! Das kann zu Verwirrung und Fehlern in der Simulation führen.&lt;br /&gt;
&lt;br /&gt;
Hardwareprobleme&lt;br /&gt;
&lt;br /&gt;
* Wenn das SPI-Modul aktiviert wird, wird &#039;&#039;&#039;NICHT&#039;&#039;&#039; automatisch SPIF gesetzt, es bleibt auf Null. Damit würde die erste Abfrage in &#039;&#039;Schiebe_alles&#039;&#039; in einer Endlosschleife hängen bleiben. Deshalb muss nach der Initialisierung des SPI-Moduls ein Dummy Byte gesendet werden, damit am Ende der Übertragung SPIF gesetzt wird&lt;br /&gt;
&lt;br /&gt;
* Da das SPI-Modul in Senderichtung nur einfach gepuffert ist, ist es nicht möglich absolut lückenlos Daten zu senden, auch wenn man mit &#039;&#039;&#039;nop&#039;&#039;&#039; eine feste minimale Zeit zwischen zwei Bytes warten würde. Zwischen zwei Bytes muss immer eine Pause von mind. 2 Systemtakten eingehalten werden.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=PWM|&lt;br /&gt;
zurücklink=AVR-Tutorial: PWM|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=SRAM|&lt;br /&gt;
vorlink=AVR-Tutorial: SRAM}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:74xx595-3.png&amp;diff=25432</id>
		<title>Datei:74xx595-3.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:74xx595-3.png&amp;diff=25432"/>
		<updated>2008-01-08T18:35:54Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:74xx595-2.png&amp;diff=25431</id>
		<title>Datei:74xx595-2.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:74xx595-2.png&amp;diff=25431"/>
		<updated>2008-01-08T18:35:00Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Datei:74xx595-1.png&amp;diff=25430</id>
		<title>Datei:74xx595-1.png</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Datei:74xx595-1.png&amp;diff=25430"/>
		<updated>2008-01-08T18:31:38Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25428</id>
		<title>AVR-Tutorial: ADC</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25428"/>
		<updated>2008-01-08T16:44:38Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Beispiele überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Was macht der ADC? ==&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht Spannungen zu messen, wird der [[ADC | &#039;&#039;&#039;A&#039;&#039;&#039;nalog &#039;&#039;&#039;D&#039;&#039;&#039;igital &#039;&#039;&#039;C&#039;&#039;&#039;onverter]] benutzt. Er konvertiert eine elektrische Spannung in eine Digitalzahl. Diese kann dann in gewohnter Weise von einem [[Mikrocontroller]] weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
== Elektronische Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Natürlich kann der ADC nicht beliebig hohe Spannungen verarbeiten. Die Grenze, bis zu der der ADC arbeitet ohne Schaden zu nehmen, liegt bei der analogen Versorgungsspannung (AVcc). Die ADC-Eingangsspannung darf diese maximal um 0.5 Volt überschreiten. Wird der Mikrocontroller also mit 5 Volt betrieben, so liegt die maximale Eingangsspannung bei ca. 5.5 Volt.&lt;br /&gt;
&lt;br /&gt;
Der Eingangswiderstand des ADC liegt in der Größenordnung von einigen Megaohm, so dass der ADC die Sgnalquelle praktisch nicht belastet. Desweiteren enthält der Mikrocontroller eine sog. &#039;&#039;&#039;Sample&amp;amp;Hold&#039;&#039;&#039; Schaltung. Dies ist wichtig, wenn sich während des Wandlungsvorgangs die Eingangsspannung verändert, da die AD-Wandlung eine bestimmte Zeit dauert. Die Sample&amp;amp;Hold-Stufe speichert zum Beginn der Wandlung die anliegende Spannung und hält sie während des Wandlungsvorgangs konstant.&lt;br /&gt;
&lt;br /&gt;
=== Beschaltung des ADC-Eingangs ===&lt;br /&gt;
&lt;br /&gt;
Um den ADC im Folgenden zu testen wird eine einfache Schaltung an den PC0-Pin des ATmega8 angeschlossen. Dies ist der ADC-Kanal 0. Bei anderen AVR-Typen liegt der entsprechende Eingang auf einem andern Pin, hier ist ein Blick ins Datenblatt angesagt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_01.gif]]&lt;br /&gt;
&lt;br /&gt;
Der Wert des [[Potentiometer]]s ist Dank des hohen Eingangswiderstandes des ADC ziemlich unkritisch. Es kann jedes Potentiometer von 1k&amp;amp;Omega; bis 1M&amp;amp;Omega; benutzt werden.&lt;br /&gt;
&lt;br /&gt;
Wenn andere Messgrößen gemessen werden sollen, so bedient man sich oft und gern des Prinzips des [[Spannungsteiler]]s. Der Sensor ist ein veränderlicher Widerstand. Zusammen mit einem zweiten, konstanten Widerstand bekannter Größe wird ein Spannungsteiler aufgebaut. Aus der Variation der durch den variablen Spannungsteiler entstehenden Spannung kann auf den Messwert zurückgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
      Vcc ----------+                Vcc ---------+&lt;br /&gt;
                    |                             |&lt;br /&gt;
                   ---                         Sensor,&lt;br /&gt;
                   | |                     der seinen Widerstand&lt;br /&gt;
                   | |                     in Abhängigkeit der&lt;br /&gt;
                   ---                     Messgröße ändert&lt;br /&gt;
                    |                             |&lt;br /&gt;
                    +------- PC0                  +-------- PC0&lt;br /&gt;
                    |                             |&lt;br /&gt;
                Sensor,                      ---&lt;br /&gt;
           der seinen Widerstand                 | |&lt;br /&gt;
           in Abhängigkeit der                   | |&lt;br /&gt;
           Messgröße ändert                      ---&lt;br /&gt;
                    |                             |&lt;br /&gt;
       GND ---------+                 GND --------+&lt;br /&gt;
&lt;br /&gt;
Die Größe des zweiten Widerstandes im Spannungsteiler richtet sich nach dem Wertebereich, in dem der Sensor seinen Wert ändert. Als Daumenregel kann man sagen, dass der Widerstand so gross sein sollte wie der Widerstand des Sensors in der Mitte des Messbereichs.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Wenn ein Temperatursensor seinen Widerstand von 0..100 Grad von 2k&amp;amp;Omega; auf 5k&amp;amp;Omega; ändert, sollte der zweite Widerstand eine Grösse von etwa (2+5)/2 = 3,5k&amp;amp;Omega; haben.&lt;br /&gt;
&lt;br /&gt;
===Referenzspannung AREF===&lt;br /&gt;
&lt;br /&gt;
Der ADC benötigt für seine Arbeit eine Referenzspannung. Dabei gibt es 2 Möglichkeiten:&lt;br /&gt;
* interne Referenzspannung&lt;br /&gt;
* externe Referenzspannung&lt;br /&gt;
&lt;br /&gt;
==== Interne Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Mittels Konfigurationsregister können beim ATmega8 verschiedene Referenzspannungen eingestellt werden. Dies umfasst die Versorungsspannung AVcc sowie eine vom AVR bereitgestellte Spannung von 2,56V (bzw. bei den neueren AVRs 1,1V, wie z.B. beim ATtiny13, ATmega48, 88, 168, ...). In beiden Fällen wird an den AREF-Pin des Prozessors ein Kondensator von 100nF als Minimalbeschaltung nach Masse angeschlossen, um die Spannung zu puffern/glätten.&lt;br /&gt;
&lt;br /&gt;
==== Externe Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Wird eine externe Referenz verwendet, so wird diese an AREF angeschlossen. Aber aufgepasst! Wenn eine Referenz in Höhe der Versorgungsspannung benutzt werden soll, so ist es besser dies über die interne Referenz zu tun. Ausser bei anderen Spannungen als 5V bzw. 2,56V gibt es eigentlich keinen Grund an AREF eine Spannungsquelle anzuschliessen. In Standardanwendungen fährt man immer besser wenn die interne Referenzspannung mit einem Kondensator an AREF benutzt wird.&lt;br /&gt;
&lt;br /&gt;
== Ein paar ADC-Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Der ADC ist ein 10-Bit ADC, d.h. er liefert Messwerte im Bereich 0 bis 1023. Liegt am Eingangskanal 0V an, so liefert der ADC einen Wert von 0. Hat die Spannung am Eingangskanal die Referenzspannung erreicht (stimmt nicht ganz), so liefert der ADC einen Wert von 1023. Unterschreitet oder überschreitet die zu messende Spannung diese Grenzen, so liefert der ADC 0 bzw. 1023. Wird die Auflösung von 10 Bit nicht benötigt, so ist es möglich die Ausgabe durch ein Konfigurationsregister so einzuschränken, dass ein leichter Zugriff auf die 8 höchstwertigen Bits möglich ist.&lt;br /&gt;
&lt;br /&gt;
Wie bei vielen analogen Schaltungen, unterliegt auch der ADC einem Rauschen. Das bedeutet, dass man nicht davon ausgehen sollte, dass der ADC bei konstanter Eingangsspannung auch immer denselben konstanten Wert ausgibt. Ein &amp;quot;Zittern&amp;quot; der niederwertigsten 2 Bits ist durchaus nicht ungewöhnlich. Besonders hervorgehoben werden soll an dieser Stelle nochmals die Qualität der Referenzspannung. Diese Qualität geht in erheblichem Maße in die Qualität der Wandelergebnisse ein. Die Beschaltung von AREF mit einem Kondensator ist die absolut notwendige Mindestbeschaltung, um eine einigermaßen akzeptable Referenzspannung zu erhalten. Reicht dies nicht aus, so kann die Qualität einer Messung durch &amp;lt;i&amp;gt;Oversampling&amp;lt;/i&amp;gt; erhöht werden. Dazu werden mehrere Messungen gemacht und deren Mittelwert gebildet.&lt;br /&gt;
&lt;br /&gt;
Oft interessiert auch der absolute Spannungspegel nicht. Im Beschaltungsbeispiel oben ist man normalerweise nicht direkt an der am Poti entstehenden Spannung interessiert. Viel mehr ist diese Spannung nur ein notwendiges Übel, um die Stellung des Potis zu bestimmen. In solchen Fällen kann die Poti-Beschaltung wie folgt abgewandelt werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_03.gif]]&lt;br /&gt;
 &lt;br /&gt;
Hier wird AREF (bei interner Referenz) als vom µC gelieferte Spannung benutzt und vom Spannungsteiler bearbeitet wieder an den µC zur Messung zurückgegeben. Dies hat den Vorteil, dass der Spannungsteiler automatisch Spannungen bis zur Höhe der Referenzspannung ausgibt, ohne dass eine externe Spannung mit AREF abgeglichen werden müsste. Selbst Schwankungen in AREF wirken sich hier nicht mehr aus, da ja das Verhältnis der Spannungsteilerspannung zu AREF immer konstant bleibt (ratiometrische Messung). Und im Grunde bestimmt der ADC ja nur dieses Verhältnis. Wird diese Variante gewählt, so muss berücksichtigt werden, dass die Ausgangsspannung an AREF nicht allzusehr belastet wird. Der Spannungsteiler muss einen Gesamtwiderstamd von deutlich über 10k&amp;amp;Omega; besitzen. Werte von 100k&amp;amp;Omega; oder höher sind anzustreben. Verwendet man anstatt AREF AVCC und schaltet auch die Referenzspannung auf AVCC um, ist die Belastung durch den Poti unkritisch, weil hier die Stromversorgung direkt zur Speisung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Ist hingegen die absolute Spannung von Interesse, so muss man darauf achten, dass ein ADC in [[Digital | digitalen]] Bereichen arbeitet ([[Quantisierung]]). An einem einfacheren Beispiel soll demonstriert werden was damit gemeint ist.&lt;br /&gt;
&lt;br /&gt;
Angenommen der ADC würde nur 5 Stufen auflösen können und AREF sei 5V:&lt;br /&gt;
&lt;br /&gt;
      Volt    Wert vom ADC&lt;br /&gt;
&lt;br /&gt;
       0 -+&lt;br /&gt;
          |         0&lt;br /&gt;
       1 -+&lt;br /&gt;
          |         1&lt;br /&gt;
       2 -+&lt;br /&gt;
          |         2&lt;br /&gt;
       3 -+&lt;br /&gt;
          |         3&lt;br /&gt;
       4 -+&lt;br /&gt;
          |         4&lt;br /&gt;
       5 -+&lt;br /&gt;
&lt;br /&gt;
Ein ADC Wert von 0 bedeutet also keineswegs, dass die zu messende Spannung exakt den Wert 0 hat. Es bedeutet lediglich, dass die Messspannung irgendwo im Bereich von 0V bis 1V liegt. Sinngemäß bedeutet daher auch das Auftreten des Maximalwertes nicht, dass die Spannung exakt AREF beträgt, sondern lediglich, dass die Messspannung sich irgendwo im Bereich der letzten Stufe (also von 4V bis 5V) bewegt.&lt;br /&gt;
&lt;br /&gt;
== Umrechnung des ADC Wertes in eine Spannung ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines &amp;quot;Bereiches&amp;quot; bestimmt sich also zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac {Referenzspannung}{Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Messwert vom ADC rechnet sich dann wie folgt in eine Spannung um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  Spannung = ADCwert \cdot \frac {Referenzspannung} {Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der ADC also mit 10 Bit an 5 V betrieben, so lauten die Umrechnungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Spannung = ADCwert \cdot \frac{5}{1024}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac{5}{1024} = 0,004883V = 4,883mV&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man genau hinsieht stellt man fest, dass sowohl die Referenzspannung als auch der Maximalwert Konstanten sind. D.h. der Quotient aus Referenzspannung und Maximalwert+1 ist konstant. Somit muss nicht immer eine Multiplikation und Division ausgeführt werden, sondern nur eine Multiplikation! Das spart viel Aufwand und Rechenzeit! Dabei kommt [[Festkommaarithmetik]] zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
== Die Steuerregister des ADC ==&lt;br /&gt;
&lt;br /&gt;
=== ADMUX ===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  | REFS1 | REFS0 | ADLAR |       |  MUX3 |  MUX2 |  MUX1 |  MUX0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* Referenzspannung &amp;lt;i&amp;gt;REFS1, REFS0&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Referenz&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;externe Referenz&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: Avcc&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;wird beim Mega8 nicht benutzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: 2.56 Volt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Ausrichtung &amp;lt;i&amp;gt;ADLAR&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADLAR&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL rechtsbündig ausgerichtet. Die 8 niederwertigsten Bits des Ergebnisses werden in ADCL abgelegt. Die verbleibenden 2 Bits des Ergebnisses werden im Register ADCH in den Bits 0 und 1 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL linksbündig ausgerichtet. Die 8 höchstwertigen Bits des Ergebnisses werden in ADCH abgelegt. Die verbleibenden 2 niederwertigen Bits werden im Register ADCL in den Bits 6 und 7 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Kanalwahl &amp;lt;i&amp;gt;MUX3, MUX2, MUX1, MUX0&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX3&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Kanal&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 0, Pin PC0&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 1, Pin PC1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 2, Pin PC2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 3, Pin PC3&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 4, Pin PC4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 5, Pin PC5&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 6, Pin ADC7 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 7, Pin ADC8 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1.23V, Vbg&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0V, GND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(*) nur in der Gehäusebauform TQFP und MLF verfügbar&lt;br /&gt;
&lt;br /&gt;
===ADCSRA===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  |  ADEN |  ADSC |  ADFR |  ADIF |  ADIE | ADPS2 | ADPS1 | ADPS0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* ADEN (ADC Enable): Mittels ADEN wird der ADC ein und ausgeschaltet. Eine 1 an dieser Bitposition schaltet den ADC ein.&lt;br /&gt;
* ADSC (ADC Start Conversion): Wird eine 1 an diese Bitposition geschrieben, so beginnt der ADC mit der Wandlung. Das Bit bleibt auf 1, solange die Wandlung im Gange ist. Wenn die Wandlung beendet ist, wird dieses Bit von der ADC Hardware wieder auf 0 gesetzt.&lt;br /&gt;
* ADFR (ADC Free Running): Wird eine 1 an ADFR geschrieben, so wird der ADC im Free Running Modus betrieben. Dabei startet der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach dem Abschluss einer Messung automatisch die nächste Messung. Die erste Messung wird ganz normal über das Setzen des &#039;&#039;&#039;ADSC&#039;&#039;&#039; Bits gestartet.&lt;br /&gt;
* ADIF (ADC Interrupt Flag): Wenn eine Messung abgeschlossen ist, wird das ADIF Bit gesetzt. Ist zusätzlich noch das &amp;lt;i&amp;gt;ADIE&amp;lt;/i&amp;gt; Bit gesetzt, so wird ein Interrupt ausgelöst und der entsprechende Interrupt Handler angesprungen.&lt;br /&gt;
* ADIE (ADC Interrupt Enable): Wird eine 1 an ADIE geschrieben, so löst der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach Beendigung einer Messung einen Interrupt aus.&lt;br /&gt;
* ADPS2, ADPS1, ADPS0 (ADC Prescaler): Mit dem Prescaler kann die ADC-Frequenz gewählt werden. Laut Datenblatt sollte diese für die optimale Auflösung zwischen 50KHz und 200kHz liegen. Ist die Wandlerfrequenz langsamer eingestellt, kann es passieren dass die eingebaute Sample &amp;amp; Hold Schaltung die Eingangsspannung nicht lange genug konstant halten kann. Ist die Frequenz aber zu schnell eingestellt, dann kann es passieren dass sich die Sample &amp;amp; Hold Schaltung nicht schnell genug an die Eingangsspannung anpassen kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Vorteiler&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;16&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;32&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;64&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;128&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Ergebnisregister ADCL und ADCH ==&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis des ADC ein 10 Bit Wert ist, passt dieser Wert naturgemäß nicht in ein einzelnes Register, das je bekanntlich nur 8 Bit breit ist. Daher wird das Ergebnis in 2 Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; abgelegt. Von den 10 Ergebnisbits sind die niederwertigsten 8 im Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; abgelegt und die noch fehlenden 2 Bits werden im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; an den niederwertigsten Bitpositionen gespeichert.&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
                             9   8       7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
Diese Zuordnung kann aber auch geändert werden: Durch setzen des &#039;&#039;&#039;ADLAR&#039;&#039;&#039; Bits im &#039;&#039;&#039;ADMUX&#039;&#039;&#039; Register wird die Ausgabe geändert zu:&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     9   8   7   6   5   4   3   2       1   0&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders dann interessant, wenn das ADC Ergebnis als 8 Bit Zahl weiterverarbeitet werden soll. In diesem Fall stehen die 8 höchstwertigen Bits bereits verarbeitungsfertig im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Beim Auslesen der ADC-Register ist zu beachten:&lt;br /&gt;
Immer zuerst &#039;&#039;&#039;ADCL&#039;&#039;&#039; und erst dann &#039;&#039;&#039;ADCH&#039;&#039;&#039; auslesen. Beim Zugriff auf &#039;&#039;&#039;ADCL&#039;&#039;&#039; wird das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register gegenüber Veränderungen vom &#039;&#039;&#039;ADC&#039;&#039;&#039; gesperrt. Erst beim nächsten Auslesen des &#039;&#039;&#039;ADCH&#039;&#039;&#039;-Registers wird diese Sperre wieder aufgehoben. Dadurch ist sichergestellt, daß die Inhalte von &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; immer aus demselben Wandlungsergebnis stammen, selbst wenn der &#039;&#039;&#039;ADC&#039;&#039;&#039; im Hintergrund selbsttätig weiterwandelt. Das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register &#039;&#039;&#039;muss&#039;&#039;&#039; ausgelesen werden!&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe als ADC-Wert ===&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm liest in einer Schleife ständig den ADC aus und verschickt des Ergebnis im Klartext (ASCII) über die [[AVR-Tutorial: UART|UART]]. Zur Verringerung des unvermeidlichen Rauschens werden 256 Messwerte herangezogen und deren Mittelwert als endgültiges Messergebnis gewertet. Dazu werden die einzelnen Messungen in den Registern temp2, temp3, temp4 als 24 Bit Zahl aufaddiert. Die Division durch 256 erfolgt dann ganz einfach dadurch, dass das Register temp2 verworfen wird und die Register temp3 und temp4 als 16 Bit Zahl aufgefasst werden. Eine Besonderheit ist noch, dass je nach dem Wert in temp2 die 16 Bit Zahl in temp3 und temp4 noch aufgerundet wird: Enthält temp2 einen Wert größer als 128, dann wird zur 16 Bit Zahl in temp3/temp4 noch 1 dazu addiert.&lt;br /&gt;
&lt;br /&gt;
In diesem Programm findet man oft die Konstruktion&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei handelt es sich um einen kleinen Trick. Um eine Konstante zu einem Register direkt addieren zu können bäuchte man dazu ein Befehl ala addi (Add Immediate, Addiere Konstante), den der AVR aber nicht hat. Ebenso gibt es kein adci (Add with carry Immediate, Addiere Konstante mit Carry Flag). Man müsste also erst eine Konstante in ein Register laden und addieren. Das kostet aber Programmspeicher, Rechenzeit und man muss ein Register zusätzlich frei haben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; 16 Bit Addition mit Konstante, ohne Cleverness&lt;br /&gt;
    ldi     temp5, low(1)&lt;br /&gt;
    add     temp3, temp5        ; addieren von 1&lt;br /&gt;
    ldi     temp5, high(1)&lt;br /&gt;
    add     temp3, temp5        ; addieren des Carry&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier greift man einfach zu dem Trick, dass eine Addition gleich der Subtraktion der negativen Werts ist. Also &amp;quot;addiere +1&amp;quot; ist gleich &amp;quot;subtrahiere -1&amp;quot;. Dafür hat der AVR zwei Befehle, subi (Substract Immediate, Subtrahiere Konstante) und sbci (Substract Immediate with carry, Subtrahiere Konstante mit Carry Flag).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r16         ; allgemeines temp Register, zur krufristigen Verwendung&lt;br /&gt;
.def temp2     = r17         ; Register für 24 Bit Addition, Lowest Byte&lt;br /&gt;
.def temp3     = r18         ; Register für 24 Bit Addition, Middle Byte&lt;br /&gt;
.def temp4     = r19         ; Register für 24 Bit Addition, Highest Byte&lt;br /&gt;
.def adlow     = r20         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def adhigh    = r21         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def messungen = r22         ; Schleifenzähler für die Messungen&lt;br /&gt;
.def ztausend  = r23         ; Zehntausenderstelle des ADC Wertes&lt;br /&gt;
.def tausend   = r24         ; Tausenderstelle des ADC Wertes&lt;br /&gt;
.def hundert   = r25         ; Hunderterstelle des ADC Wertes&lt;br /&gt;
.def zehner    = r26         ; Zehnerstelle des ADC Wertes&lt;br /&gt;
.def zeichen   = r27         ; Zeichen zur Ausgabe auf den UART&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programm los&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(RAMEND)                  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
;UART Initalisierung&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(UBRR_VAL)                    ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, TXEN                         ; TX einschalten&lt;br /&gt;
 &lt;br /&gt;
; ADC initialisieren: Single Conversion, Vorteiler 128&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;REFS0)                   ; Kanal 0, interne Referenzspannung 5V&lt;br /&gt;
    out     ADMUX, temp1&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
    out     ADCSRA, temp1&lt;br /&gt;
 &lt;br /&gt;
Main:&lt;br /&gt;
    clr     temp1&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
&lt;br /&gt;
    ldi     messungen, 0        ; 256 Schleifendurchläufe&lt;br /&gt;
 &lt;br /&gt;
; neuen ADC-Wert lesen  (Schleife - 256 mal)&lt;br /&gt;
&lt;br /&gt;
sample_adc:&lt;br /&gt;
    sbi     ADCSRA, ADSC        ; den ADC starten&lt;br /&gt;
 &lt;br /&gt;
wait_adc:&lt;br /&gt;
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht&lt;br /&gt;
    rjmp    wait_adc&lt;br /&gt;
 &lt;br /&gt;
; ADC einlesen:&lt;br /&gt;
&lt;br /&gt;
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen&lt;br /&gt;
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte&lt;br /&gt;
 &lt;br /&gt;
; alle 256 ADC-Werte addieren&lt;br /&gt;
; dazu wird mit den Registern temp4, temp3 und temp2 ein&lt;br /&gt;
; 24-Bit breites Akkumulationsregister gebildet, in dem&lt;br /&gt;
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden&lt;br /&gt;
&lt;br /&gt;
    add     temp2, adlow        ; addieren&lt;br /&gt;
    adc     temp3, adhigh       ; addieren über Carry&lt;br /&gt;
    adc     temp4, temp1        ; addieren über Carry, temp1 enthält 0&lt;br /&gt;
    dec     messungen           ; Schleifenzähler MINUS 1&lt;br /&gt;
    brne    sample_adc          ; wenn noch keine 256 ADC Werte -&amp;gt; nächsten Wert einlesen&lt;br /&gt;
 &lt;br /&gt;
; Aus den 256 Werten den Mittelwert berechnen&lt;br /&gt;
; Mathematisch eine Division durch 256&lt;br /&gt;
; Da aber 2^8 = 256 ist ist da einfach durch das weglassen des niederwertigsten Bytes&lt;br /&gt;
; erreicht werden&lt;br /&gt;
;&lt;br /&gt;
; allerdings wird der Wert noch gerundet&lt;br /&gt;
&lt;br /&gt;
    cpi     temp2,128           ; &amp;quot;Kommastelle&amp;quot; kleiner als 128 ?&lt;br /&gt;
    brlo    no_round            ; ist kleiner ==&amp;gt; Sprung&lt;br /&gt;
 &lt;br /&gt;
; Aufrunden&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
 &lt;br /&gt;
no_round:&lt;br /&gt;
&lt;br /&gt;
;   Ergebnis nach adlow und adhigh kopieren&lt;br /&gt;
;   damit die temp Register frei werden&lt;br /&gt;
&lt;br /&gt;
    mov     adlow, temp3&lt;br /&gt;
    mov     adhigh, temp4&lt;br /&gt;
 &lt;br /&gt;
;in ASCII umwandeln&lt;br /&gt;
; Division durch mehrfache Subtraktion&lt;br /&gt;
&lt;br /&gt;
    ldi     ztausend, &#039;0&#039;-1     ; Ziffernzähler direkt als ASCII Code&lt;br /&gt;
Z_ztausend:&lt;br /&gt;
    inc     ztausend&lt;br /&gt;
    subi    adlow, low(10000)   ; -10,000&lt;br /&gt;
    sbci    adhigh, high(10000) ; 16 Bit&lt;br /&gt;
    brcc    Z_ztausend&lt;br /&gt;
                                    &lt;br /&gt;
    subi    adlow, low(-10000)  ; nach Unterlauf wieder einmal addieren&lt;br /&gt;
    sbci    adhigh, high(-10000); +10,000&lt;br /&gt;
 &lt;br /&gt;
    ldi     tausend, &#039;0&#039;-1      ; Ziffernzähler direkt als ASCII Code&lt;br /&gt;
Z_tausend:&lt;br /&gt;
    inc     tausend&lt;br /&gt;
    subi    adlow, low(1000)    ; -1,000&lt;br /&gt;
    sbci    adhigh, high(1000)  ; 16 Bit&lt;br /&gt;
    brcc    Z_tausend&lt;br /&gt;
                                    &lt;br /&gt;
    subi    adlow, low(-1000)   ; nach Unterlauf wieder einmal addieren&lt;br /&gt;
    sbci    adhigh, high(-1000) ; +1,000&lt;br /&gt;
 &lt;br /&gt;
    ldi     hundert, &#039;0&#039;-1      ; Ziffernzähler direkt als ASCII Code&lt;br /&gt;
Z_hundert:&lt;br /&gt;
    inc     hundert&lt;br /&gt;
    subi    adlow, low(100)     ; -100&lt;br /&gt;
    sbci    adhigh, high(100)   ; 16 Bit&lt;br /&gt;
    brcc    Z_hundert&lt;br /&gt;
                                    &lt;br /&gt;
    subi    adlow, low(-100)    ; nach Unterlauf wieder einmal addieren&lt;br /&gt;
    sbci    adhigh, high(-100)  ; +100&lt;br /&gt;
 &lt;br /&gt;
    ldi     zehner, &#039;0&#039;-1       ; Ziffernzähler direkt als ASCII Code&lt;br /&gt;
Z_zehner:&lt;br /&gt;
    inc     zehner&lt;br /&gt;
    subi    adlow, low(10)      ; -10&lt;br /&gt;
    sbci    adhigh, high(10)    ; 16 Bit&lt;br /&gt;
    brcc    Z_zehner&lt;br /&gt;
                                    &lt;br /&gt;
    subi    adlow, low(-10)     ; nach Unterlauf wieder einmal addieren&lt;br /&gt;
    sbci    adhigh, high(-10)   ; +10&lt;br /&gt;
&lt;br /&gt;
    subi    adlow, -&#039;0&#039;         ; adlow enthält die Einer, Umwandlung in ASCII&lt;br /&gt;
 &lt;br /&gt;
;an UART Senden&lt;br /&gt;
&lt;br /&gt;
    mov     zeichen, ztausend   ; Zehntausender Stelle&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, tausend    ; Tausender Stelle ausgeben&lt;br /&gt;
    rcall   transmit    &lt;br /&gt;
    mov     zeichen, hundert    ; Hunderter Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, zehner     ; Zehner Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, adlow      ; Einer Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 13         ; CR, Carrige Return (Wagenrücklauf)&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 10         ; LF, Line Feed (Neue Zeile)&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
 &lt;br /&gt;
    rjmp    Main&lt;br /&gt;
 &lt;br /&gt;
transmit:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    transmit&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe als Spannungswert ===&lt;br /&gt;
&lt;br /&gt;
Das zweite Beispiel ist schon um einiges größer. Hier wird der gemittelte ADC-Wert in eine Spannung umgerechnet. Dazu wird [[Festkommaarithmetik]] verwendet. Die Daten sind in diesem Fall&lt;br /&gt;
&lt;br /&gt;
* Referenzspannung : 5V&lt;br /&gt;
* alte Auflösung   : 5V / 1024 = 4,8828125mV&lt;br /&gt;
* neue Auflösung   : 1mV&lt;br /&gt;
&lt;br /&gt;
-&amp;gt; Faktor = 4,8828125mV / 1mV = 4,8828125&lt;br /&gt;
&lt;br /&gt;
Der Faktor wird dreimal mit 10 multipliziert und das Ergebnis auf 4883 gerundet. Die neue Auflösung wird dreimal durch 10 dividiert und beträgt 1&amp;amp;mu;V. Der relative Fehler beträgt&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; F_r = \frac {4883}{4882,8125}-1 = 0,00384% = \frac {1}{26042}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Fehler ist absolut vernachlässigbar. Nach der Multiplikation des ADC-Wertes mit 4883 liegt die gemessene Spannung in der Einheit &amp;amp;mu;V vor. Vorsicht! Das ist &#039;&#039;&#039;nicht&#039;&#039;&#039; die reale [[Auflösung und Genauigkeit]], nur rein mathematisch bedingt. Für maximale Genauigkeit sollte man die Versorgungsspannung AVCC, welche hier gleichzeitig als Referenzspannung dient, exakt messen, die Rechnung nachvollziehen und den Wert im Quelltext eintragen. Damit führt man eine einfach Einpunktkalibrierung durch.&lt;br /&gt;
&lt;br /&gt;
Da das Programm schon um einiges größer und komplexer ist, wurde es im Vergleich zur Vorgängerversion geändert. Die Multiplikation sowie die Umwandung der Zahl in einen ASCII-String sind als Unterprogramme geschrieben, dadurch erhält man wesentlich mehr Überblick im Hauptprogramm und die Wiederverwendung in anderen Programmen vereinfacht sich. Ausserdem wird der String im RAM gespeichert und nicht mehr in CPU-Registern. Die Berechung der einzelnen Ziffern erfolgt über ein Schleife, das ist kompakter und übersichtlicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def z0        = r1          ; Zahl für Integer -&amp;gt; ASCII Umwandlung&lt;br /&gt;
.def z1        = r2&lt;br /&gt;
.def z2        = r3&lt;br /&gt;
.def z3        = r4&lt;br /&gt;
.def temp1     = r16         ; allgemeines Register, zur kurzfristigen Verwendung&lt;br /&gt;
.def temp2     = r17         ; Register für 24 Bit Addition, niederwertigstes Byte (LSB)&lt;br /&gt;
.def temp3     = r18         ; Register für 24 Bit Addition, mittlerers Byte&lt;br /&gt;
.def temp4     = r19         ; Register für 24 Bit Addition, höchstwertigstes Byte (MSB)&lt;br /&gt;
.def adlow     = r20         ; Ergebnis vom ADC-Mittelwert der 256 Messungen&lt;br /&gt;
.def adhigh    = r21         ; Ergebnis vom ADC-Mittelwert der 256 Messungen&lt;br /&gt;
.def messungen = r22         ; Schleifenzähler für die Messungen&lt;br /&gt;
.def zeichen   = r23         ; Zeichen zur Ausgabe auf den UART&lt;br /&gt;
.def temp5     = r24&lt;br /&gt;
.def temp6     = r25&lt;br /&gt;
&lt;br /&gt;
; Faktor für Umrechung des ADC-Wertes in Spannung&lt;br /&gt;
; = (Referenzspannung / 1024 ) * 100000&lt;br /&gt;
; = 5V / 1024 * 1.000.000&lt;br /&gt;
.equ Faktor = 4883&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
; RAM&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Puffer: .byte 10&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm los&lt;br /&gt;
.cseg&lt;br /&gt;
.org 0&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(RAMEND)                  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
;UART Initalisierung&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(UBRR_VAL)                ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, TXEN                         ; TX einschalten&lt;br /&gt;
 &lt;br /&gt;
; ADC initialisieren: Single Conversion, Vorteiler 128&lt;br /&gt;
; Kanal 0, interne Referenzspannung AVCC&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;REFS0)                   &lt;br /&gt;
    out     ADMUX, temp1&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
    out     ADCSRA, temp1&lt;br /&gt;
 &lt;br /&gt;
Hauptschleife:&lt;br /&gt;
    clr     temp1&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
&lt;br /&gt;
    ldi     messungen, 0        ; 256 Schleifendurchläufe&lt;br /&gt;
 &lt;br /&gt;
; neuen ADC-Wert lesen  (Schleife - 256 mal)&lt;br /&gt;
&lt;br /&gt;
adc_messung:&lt;br /&gt;
    sbi     ADCSRA, ADSC        ; den ADC starten&lt;br /&gt;
 &lt;br /&gt;
adc_warten:&lt;br /&gt;
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht&lt;br /&gt;
    rjmp    adc_warten&lt;br /&gt;
 &lt;br /&gt;
; ADC einlesen:&lt;br /&gt;
&lt;br /&gt;
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen&lt;br /&gt;
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte&lt;br /&gt;
 &lt;br /&gt;
; alle 256 ADC-Werte addieren&lt;br /&gt;
; dazu wird mit den Registern temp4, temp3 und temp2 ein&lt;br /&gt;
; 24-Bit breites Akkumulationsregister gebildet, in dem&lt;br /&gt;
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden&lt;br /&gt;
&lt;br /&gt;
    add     temp2, adlow        ; addieren&lt;br /&gt;
    adc     temp3, adhigh       ; addieren über Carry&lt;br /&gt;
    adc     temp4, temp1        ; addieren über Carry, temp1 enthält 0&lt;br /&gt;
    dec     messungen           ; Schleifenzähler MINUS 1&lt;br /&gt;
    brne    adc_messung         ; wenn noch keine 256 ADC Werte -&amp;gt; nächsten Wert einlesen&lt;br /&gt;
 &lt;br /&gt;
; Aus den 256 Werten den Mittelwert berechnen&lt;br /&gt;
; Bei 256 Werten ist das ganz einfach: Das niederwertigste Byte&lt;br /&gt;
; (im Register temp2) fällt einfach weg&lt;br /&gt;
;&lt;br /&gt;
; allerdings wird der Wert noch gerundet&lt;br /&gt;
&lt;br /&gt;
    cpi     temp2,128           ; &amp;quot;Kommastelle&amp;quot; kleiner als 128 ?&lt;br /&gt;
    brlo    nicht_runden        ; ist kleiner ==&amp;gt; Sprung&lt;br /&gt;
 &lt;br /&gt;
; Aufrunden&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
 &lt;br /&gt;
nicht_runden:&lt;br /&gt;
&lt;br /&gt;
;   Ergebnis nach adlow und adhigh kopieren&lt;br /&gt;
;   damit die temp Register frei werden&lt;br /&gt;
&lt;br /&gt;
    mov     adlow, temp3&lt;br /&gt;
    mov     adhigh, temp4&lt;br /&gt;
&lt;br /&gt;
; in Spannung umrechnen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp5,low(Faktor)&lt;br /&gt;
    ldi     temp6,high(Faktor)&lt;br /&gt;
    rcall   mul_16x16&lt;br /&gt;
&lt;br /&gt;
; in ASCII umwandeln&lt;br /&gt;
&lt;br /&gt;
    ldi     XL, low(Puffer)&lt;br /&gt;
    ldi     XH, high(Puffer)&lt;br /&gt;
    rcall   Int_to_ASCII&lt;br /&gt;
 &lt;br /&gt;
;an UART Senden&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL, low(Puffer+3)&lt;br /&gt;
    ldi     ZH, high(Puffer+3)&lt;br /&gt;
    ldi     temp1, 1&lt;br /&gt;
    rcall   sende_zeichen       ; eine Vorkommastelle ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, &#039;,&#039;        ; Komma ausgeben&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, 3            ; Drei Nachkommastellen ausgeben&lt;br /&gt;
    rcall   sende_zeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, &#039;V&#039;        ; Volt Zeichen ausgeben&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, 10         ; New Line Steuerzeichen&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, 13         ; Carrige Return Steuerzeichen&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    rjmp    Hauptschleife&lt;br /&gt;
&lt;br /&gt;
; Ende des Hauptprogramms&lt;br /&gt;
&lt;br /&gt;
; Unterprogramme&lt;br /&gt;
 &lt;br /&gt;
 ; ein Zeichen per UART senden&lt;br /&gt;
&lt;br /&gt;
sende_einzelzeichen:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    sende_einzelzeichen&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; mehrere Zeichen ausgeben, welche durch Z adressiert werden&lt;br /&gt;
; Anzahl in temp1&lt;br /&gt;
&lt;br /&gt;
sende_zeichen:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    sende_zeichen&lt;br /&gt;
    ld      zeichen, Z+         ; Zeichen laden&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    dec     temp1&lt;br /&gt;
    brne    sende_zeichen&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; 32 Bit Zahl in ASCII umwandeln&lt;br /&gt;
; Zahl liegt in temp1..4&lt;br /&gt;
; Ergebnis ist ein 10stelliger ASCII String, welcher im SRAM abgelegt wird&lt;br /&gt;
; Adressierung über X Pointer&lt;br /&gt;
; mehrfache Subtraktion wird als Ersatz für eine Division durchgeführt.&lt;br /&gt;
&lt;br /&gt;
Int_to_ASCII:&lt;br /&gt;
    &lt;br /&gt;
    push    ZL                      ; Register sichern&lt;br /&gt;
    push    ZH&lt;br /&gt;
    push    temp5&lt;br /&gt;
    push    temp6&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL,low(Tabelle*2)       ; Zeiger auf Tabelle&lt;br /&gt;
    ldi     ZH,high(Tabelle*2)&lt;br /&gt;
    ldi     temp5, 10               ; Schleifenzähler&lt;br /&gt;
&lt;br /&gt;
Int_to_ASCII_schleife:&lt;br /&gt;
    ldi     temp6, -1+&#039;0&#039;           ; Ziffernzähler zählt direkt im ASCII Code &lt;br /&gt;
    lpm     z0,Z+                   ; Nächste Zahl laden&lt;br /&gt;
    lpm     z1,Z+&lt;br /&gt;
    lpm     z2,Z+&lt;br /&gt;
    lpm     z3,Z+&lt;br /&gt;
&lt;br /&gt;
Int_to_ASCII_ziffer:&lt;br /&gt;
    inc     temp6                   ; Ziffer erhöhen&lt;br /&gt;
    sub     temp1, z0               ; Zahl subrahieren&lt;br /&gt;
    sbc     temp2, z1               ; 32 Bit&lt;br /&gt;
    sbc     temp3, z2&lt;br /&gt;
    sbc     temp4, z3&lt;br /&gt;
    brge    Int_to_ASCII_ziffer     ; noch kein Unterlauf, nochmal&lt;br /&gt;
&lt;br /&gt;
    add     temp1, z0               ; Unterlauf, eimal wieder addieren&lt;br /&gt;
    adc     temp2, z1               ; 32 Bit&lt;br /&gt;
    adc     temp3, z2&lt;br /&gt;
    adc     temp4, z3                                            &lt;br /&gt;
    st      X+,temp6                ; Ziffer speichern&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    brne    Int_to_ASCII_schleife   ; noch eine Ziffer?&lt;br /&gt;
&lt;br /&gt;
    pop     temp6&lt;br /&gt;
    pop     temp5&lt;br /&gt;
    pop     ZH&lt;br /&gt;
    pop     ZL                      ; Register wieder herstellen&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; Tabelle mit Zahlen für die Berechung der Ziffern&lt;br /&gt;
; 1 Milliarde bis 1&lt;br /&gt;
Tabelle:&lt;br /&gt;
.dd 1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1&lt;br /&gt;
&lt;br /&gt;
; 16 Bit Wert in Spannung umrechnen&lt;br /&gt;
;&lt;br /&gt;
; = 16Bitx16Bit=32 Bit Multiplikation&lt;br /&gt;
; = vier 8x8 Bit Multiplikationen&lt;br /&gt;
;&lt;br /&gt;
; adlow/adhigh * temp5/temp6&lt;br /&gt;
&lt;br /&gt;
mul_16x16:&lt;br /&gt;
    push    zeichen&lt;br /&gt;
    clr     temp1                   ; 32 Bit Akku löschen&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
    clr     zeichen                 ; Null, für Carry-Addition&lt;br /&gt;
&lt;br /&gt;
    mul     adlow, temp5            ; erste Multiplikation&lt;br /&gt;
    add     temp1, r0               ; und akkumulieren&lt;br /&gt;
    adc     temp2, r1&lt;br /&gt;
&lt;br /&gt;
    mul     adhigh, temp5           ; zweite Multiplikation&lt;br /&gt;
    add     temp2, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp3, r1&lt;br /&gt;
&lt;br /&gt;
    mul     adlow, temp6            ; dritte Multiplikation&lt;br /&gt;
    add     temp2, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp3, r1&lt;br /&gt;
    adc     temp4, zeichen          ; carry addieren&lt;br /&gt;
&lt;br /&gt;
    mul     adhigh, temp6           ; vierte Multiplikation&lt;br /&gt;
    add     temp3, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp4, r1&lt;br /&gt;
&lt;br /&gt;
    pop     zeichen&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für alle, die es besonders eilig haben gibt es hier eine geschwindigkeitsoptimierte Version der Integer in ASCII Umwandlung. Zunächst wird keine Schleife verwendet sondern alle Stufen der Schleife direkt hingeschrieben. Das braucht zwar mehr Programmspeicher, ist aber schneller. Ausserdem wird abwechselnd subtrahiert uund addiert, dadurch entfällt das immer wieder notwendige addieren nach dem Unterlauf. Zu guter Letzt werden die Berechnungen nur mit der minimal notwenigen Wortbreite durchgeführt. Am Anfang mit 32 Bit, dann nur noch mit 16 bzw. 8 Bit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; 32 Bit Zahl in ASCII umwandeln&lt;br /&gt;
; geschwindigkeitsoptimierte Version&lt;br /&gt;
; Zahl liegt in temp1..4&lt;br /&gt;
; Ergebnis ist ein 10stelliger ASCII String, welcher im SRAM abgelegt wird&lt;br /&gt;
; Adressierung über X Pointer&lt;br /&gt;
&lt;br /&gt;
Int_to_ASCII:&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a1ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,BYTE1(1000000000) ; - 1.000.000.000&lt;br /&gt;
    sbci    temp2,BYTE2(1000000000)&lt;br /&gt;
    sbci    temp3,BYTE3(1000000000)&lt;br /&gt;
    sbci    temp4,BYTE4(1000000000)&lt;br /&gt;
    brcc    _a1ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a2ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,BYTE1(-100000000) ; + 100.000.000&lt;br /&gt;
    sbci    temp2,BYTE2(-100000000)&lt;br /&gt;
    sbci    temp3,BYTE3(-100000000)&lt;br /&gt;
    sbci    temp4,BYTE4(-100000000)&lt;br /&gt;
    brcs    _a2ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a3ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(10000000)     ; - 10.000.000&lt;br /&gt;
    sbci    temp2,high(10000000)&lt;br /&gt;
    sbci    temp3,BYTE3(10000000)&lt;br /&gt;
    brcc    _a3ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a4ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-1000000)     ; + 1.000.000&lt;br /&gt;
    sbci    temp2,high(-1000000)&lt;br /&gt;
    sbci    temp3,BYTE3(-1000000)&lt;br /&gt;
    brcs    _a4ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a5ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(100000)       ; -100.000&lt;br /&gt;
    sbci    temp2,high(100000)&lt;br /&gt;
    sbci    temp3,BYTE3(100000)&lt;br /&gt;
    brcc    _a5ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a6ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-10000)       ; +10,000&lt;br /&gt;
    sbci    temp2,high(-10000)&lt;br /&gt;
    sbci    temp3,BYTE3(-10000)&lt;br /&gt;
    brcs    _a6ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern &lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a7ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(1000)         ; -1000&lt;br /&gt;
    sbci    temp2,high(1000)&lt;br /&gt;
    brcc    _a7ser&lt;br /&gt;
 &lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a8ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-100)         ; +100&lt;br /&gt;
    sbci    temp2,high(-100)&lt;br /&gt;
    brcs    _a8ser&lt;br /&gt;
 &lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a9ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1, 10               ; -10&lt;br /&gt;
    brcc    _a9ser&lt;br /&gt;
    &lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a10ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1, -1               ; +1&lt;br /&gt;
    brcs    _a10ser&lt;br /&gt;
&lt;br /&gt;
    st      X+,temp5                ; im Puffer speichern&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Uhr|&lt;br /&gt;
zurücklink=AVR-Tutorial: Uhr|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Tasten|&lt;br /&gt;
vorlink=AVR-Tutorial: Tasten}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Logische_Verkn%C3%BCpfungen&amp;diff=25414</id>
		<title>Logische Verknüpfungen</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Logische_Verkn%C3%BCpfungen&amp;diff=25414"/>
		<updated>2008-01-07T20:53:48Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Änderung 25393 von 82.83.71.54 (Diskussion) wurde rückgängig gemacht.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;====UND====&lt;br /&gt;
Verknüpfung (Konjunktion)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = a \wedge b&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;[[Bild:AND.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;&lt;br /&gt;
  &amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; &#039;&#039;&#039; a &#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; &#039;&#039;&#039; b &#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; &#039;&#039;&#039; x &#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;td&amp;gt;----&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td&amp;gt;----&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td&amp;gt;+&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;td&amp;gt;----&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
    &amp;lt;tr&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt; | &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
    &amp;lt;/tr&amp;gt;&lt;br /&gt;
  &amp;lt;/table&amp;gt;&lt;br /&gt;
  &amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
 -.-*&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====ODER====&lt;br /&gt;
Verknüpfung (Disjunktion)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = a \vee b&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:ODER.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====NICHT====&lt;br /&gt;
Verknüpfung (Negation)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = \overline{a}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:NOT.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;-&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====NAND====&lt;br /&gt;
Verknüpfung&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = \overline{a \wedge b}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:NAND.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====NOR====&lt;br /&gt;
Verknüpfung (Konjunktion)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = \overline{a \vee b}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:NOR.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====XOR====&lt;br /&gt;
Exklusiv-ODER-Verknüpfung (Antivalenz)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = (a \wedge \overline{b}) \vee (\overline{a} \wedge b)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:XOR.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
====XNOR====&lt;br /&gt;
Exklusiv-NOR-Verknüpfung (Äquivalenz)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = (a \wedge b) \vee (\overline{a} \wedge \overline{b})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:XNOR.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sperrgatter&#039;&#039;&#039; (Inhibition)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = \overline{a} \wedge b&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:Inhibition.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Subjunktion&#039;&#039;&#039; (Implikation)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;x = \overline{a} \vee b&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;[[Bild:Implikation.png]]&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;a&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;b&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt; &#039;&#039;&#039;x&#039;&#039;&#039; &amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
 &amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&lt;br /&gt;
 &amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=25372</id>
		<title>AVR-Tutorial: 7-Segment-Anzeige</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_7-Segment-Anzeige&amp;diff=25372"/>
		<updated>2008-01-06T13:25:54Z</updated>

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

		<summary type="html">&lt;p&gt;FalkB: Erweiterung für I/O Register per lds/sts&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bits setzen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Eins gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#ODER | ODER]]-Verknüpfung erreicht.  Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden auf &#039;1&#039; gesetzt. Alle Bits, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbr r16, 0b11110000     ; setzt Bits 4-7 in r16, ist ein Pseudobefehl&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
ori r16, 0b11110000     ; setzt Bits 4-7 in r16, ori ist sbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
sbi PORTB, 5            ; setzt Bit 5 in PortB&lt;br /&gt;
sbi PORTB, PB5          ; identisch, besser lesbar&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..0x1F&lt;br /&gt;
&lt;br /&gt;
                        ; für I/O Register mit I/O Adresse 0x20..0x3F muss&lt;br /&gt;
                        ; in/out verwendet werden&lt;br /&gt;
in  r16, TIMSK          ; setzt Bit TOIE1 in TIMSK&lt;br /&gt;
sbr r16, (1&amp;lt;&amp;lt;TOIE1)    &lt;br /&gt;
out UCSR0B, r16&lt;br /&gt;
&lt;br /&gt;
                        ; für I/O Register oberhalb der I/O Adresse 0x3F muss&lt;br /&gt;
                        ; lds/sts verwednet werden&lt;br /&gt;
                        ; setzt Bit RXCIE0 in UCSR0B&lt;br /&gt;
lds r16, UCSR0B &lt;br /&gt;
sbr r16, (1&amp;lt;&amp;lt;RXCIE0)    &lt;br /&gt;
sts UCSR0B, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von sbr als &amp;quot;setze Bit 2 und 0&amp;quot; gedeutet (=0b00000101), während sbi sie als &amp;quot;setzte Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;sbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine ODER-Verknüpfung, während der Befehlt &#039;&#039;&#039;sbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt. Darauf sind auch die Includefiles von Atmel im AVR-Studio (Assembler) als auch [[WinAVR]] (C) ausgelegt. Die Namen der Bits sind als Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; definiert. Das ist wichtig, wenn man Register von grossen AVRs manipuliert, z.B. ATmega48. Hier muss aus der Bitnummer über eine Schiebeoperation erst das Bit&#039;&#039;&#039;muster&#039;&#039;&#039; gemacht werden.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB |= 0xF0;   // Kurzschreibweise, entspricht PORTB = PORTB | 0xF0 also bitweises ODER.&lt;br /&gt;
&lt;br /&gt;
/* übersichtlicher mittels Bit-Definitionen */&lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1&lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
PORTB |= ((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // setzt Bit 0 und 2 in PORTB auf &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben.  Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft, also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Diese Maske wird mit dem aktuellen Inhalt von PORTB bitweise ODER-verknüpft und das Ergebnis PORTB mittels &#039;|=&#039; wieder zugewiesen. Ist PORTB vorher z.B. 0b01111010, dann ist der Inhalt nach der Operation 0b01111010 &#039;&#039;or&#039;&#039; 0b00000101 = 0b01111111, die gewünschten Bits sind somit gesetzt!&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Will man das gezeigte Beispiel der Bitmanipulation auf größere Datentypen anwenden, ist zu beachten, dass der Compiler in der Operation (1 &amp;lt;&amp;lt; MEINBIT1) stillschweigend die 1 als Integer Type ansieht. Beim AVR-GCC bedeutet das 16-Bit und die folgende Operation bringt ggf. nicht das gewünschte Ergebnis. (Stichwort: &amp;quot;Integer Promotion&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Angenommen Bit 15 soll in einer 32-Bit weiten Variable gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define MEINBIT15 15&lt;br /&gt;
#define MEINBIT42 42&lt;br /&gt;
&lt;br /&gt;
uint32_t reg_32;&lt;br /&gt;
uint64_t reg_64;&lt;br /&gt;
&lt;br /&gt;
reg_32 |= (1 &amp;lt;&amp;lt; MEINBIT15);              /* FEHLER: Setzt die Bits 31 - 15, da ((int)1 &amp;lt;&amp;lt; 15) == 0xFFFF8000 */&lt;br /&gt;
&lt;br /&gt;
reg_32 |= ((uint32_t)1 &amp;lt;&amp;lt; MEINBIT15);    /* Hier wird nur Bit 15 gesetzt. */&lt;br /&gt;
reg_32 |= (1L &amp;lt;&amp;lt; MEINBIT15);             /* andere Schreibweise. */&lt;br /&gt;
reg_64 |= (1LL &amp;lt;&amp;lt; MEINBIT42);            /* Hier wird nur Bit 42 gesetzt,&lt;br /&gt;
                                            andere Schreibweise für 64 Bit (long long). */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bit löschen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Null gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#UND | UND]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;0&#039; sind, werden auf &#039;0&#039; gesetzt. Alle Bits, die in der Maske auf &#039;1&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cbr r16, 0b11110000     ; löscht Bits 0-3 in r16, setzt sie auf 0&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, 0b11110000    ; löscht Bits 0-3 in r16, andi ist identisch mit dem Pseudobefehl cbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, ~0b00001111   ; andere Schreibweise, hier wird die Bitmaske durch ~ invertiert&lt;br /&gt;
                        ; dadurch kann man einfach alle zu löschenden Bit als &#039;1&#039; angeben&lt;br /&gt;
                        ; so wie bei den Bitmasken für das setzen von Bits (positive Logik)&lt;br /&gt;
&lt;br /&gt;
cbi PORTB, 5            ; löscht Bit 5 in PortB&lt;br /&gt;
cbi PORTB, PB5          ; identisch, besser lesbar&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..31&lt;br /&gt;
&lt;br /&gt;
                        ; für I/O Register mit I/O Adresse 0x20..0x3F muss&lt;br /&gt;
                        ; in/out verwendet werden&lt;br /&gt;
in  r16, TIMSK          ; löscht Bit TOIE1 in TIMSK&lt;br /&gt;
cbr r16, ~(1&amp;lt;&amp;lt;TOIE1)    &lt;br /&gt;
out UCSR0B, r16&lt;br /&gt;
&lt;br /&gt;
                        ; für I/O Register oberhalb der I/O Adresse 0x3F muss&lt;br /&gt;
                        ; lds/sts verwednet werden&lt;br /&gt;
                        ; löscht Bit RXCIE0 in UCSR0B&lt;br /&gt;
lds r16, UCSR0B &lt;br /&gt;
cbr r16, ~(1&amp;lt;&amp;lt;RXCIE0)    &lt;br /&gt;
sts UCSR0B, r16&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt. Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von cbr als &amp;quot;lösche Bit 7,6,5,4,3 und 1&amp;quot; gedeutet, während cbi sie als &amp;quot;lösche Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;cbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine UND-Verknüpfung, während der Befehlt &#039;&#039;&#039;cbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt. Darauf sind auch die Includefiles von Atmel im AVR-Studio (Assembler) als auch [[WinAVR]] (C) ausgelegt. Die Namen der Bits sind als Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; definiert. Das ist wichtig, wenn man Register von grossen AVRs manipuliert, z.B. ATmega48. Hier muss aus der Bitnummer über eine Schiebeoperation erst das Bit&#039;&#039;&#039;muster&#039;&#039;&#039; gemacht werden.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= 0xF0;   // entspricht PORTB = PORTB &amp;amp; 0xF0 also bitweises UND;&lt;br /&gt;
                 // Bits 0-3 (das &amp;quot;niederwertige&amp;quot; Nibble) werden geloescht &lt;br /&gt;
&lt;br /&gt;
/* uebersichtlicher mittels Bit-Definitionen */ &lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1  &lt;br /&gt;
#define MEINBIT2 2  &lt;br /&gt;
&lt;br /&gt;
PORTB &amp;amp;= ~((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // löscht Bit 0 und 2 in PORTB&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Durch &#039;~&#039; wird der Wert in der Klammer invertiert, aus 0b00000101 wird 0b11111010. Schließlich wird durch &#039;&amp;amp;=&#039; mit dem aktuellen Wert von PORTB UND-verknüpft und das Ergebnis wieder PORTB zugewiesen. Ist PORTB vorher z.B. 0b01111111, dann ist der Inhalt nach der Operation 0b011111111 &#039;&#039;and&#039;&#039; 0b11111010 = 0b01111010, die gewünschten Bits (0 und 2) sind somit gelöscht. &lt;br /&gt;
&lt;br /&gt;
Die C-Ausdrücke sehen auf den ersten Blick etwas &amp;quot;erschreckend&amp;quot; aus und sind mehr &amp;quot;Tipparbeit&amp;quot;, funktionieren aber universell und sind nach einiger Gewöhnung deutlicher und nachvollziehbarer als &amp;quot;handoptimierte&amp;quot; Konstanten.&lt;br /&gt;
&lt;br /&gt;
=== Niederwertigstes gesetztes Bit löschen (Standard C) ===&lt;br /&gt;
&lt;br /&gt;
Folgender Code löscht von allen 1-Bits in einer Integer-Variable das niederwertigste, unabhängig von der Position desselben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 01101000 -&amp;gt; 01100000&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t byte;&lt;br /&gt;
&lt;br /&gt;
byte = irgendwas();&lt;br /&gt;
&lt;br /&gt;
byte = byte &amp;amp; (byte - 1); /* Diese seltsame Operation löscht das&lt;br /&gt;
                             niederwertigste 1-Bit */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann bspw. zur schnellen Paritätsgenerierung eingesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t pareven(uint8_t byte) {&lt;br /&gt;
  uint8_t par = 0;&lt;br /&gt;
&lt;br /&gt;
  while(byte) {&lt;br /&gt;
    byte = byte &amp;amp; (byte - 1);&lt;br /&gt;
    par = !par;&lt;br /&gt;
  }&lt;br /&gt;
  return par;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das genannte gilt natürlich nicht nur für 8-Bit-Integers, sondern für beliebige, vom Compiler unterstützte Wortlängen.&lt;br /&gt;
&lt;br /&gt;
== Bit toggeln ==&lt;br /&gt;
&lt;br /&gt;
Toggeln bedeutet den Wert eines Bits invertiert. Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] invertiert (getoggelt) werden sollen, wird die durch eine [[AVR-Tutorial:_Logik#XOR_.28Exlusives_Oder.29 | XOR]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden invertiert. Alle Bits, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR Assembler ===&lt;br /&gt;
&lt;br /&gt;
Bei [[AVR]]s erlaubt dies z.B. folgender Assemblercode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  sbic  PortB, 0    ; skip if bit is cleared&lt;br /&gt;
  rjmp  ClrBitNow   ; jump&lt;br /&gt;
  sbi   PortB, 0    ; set bit (if bit was cleared)&lt;br /&gt;
  rjmp  BitReady    ; jump&lt;br /&gt;
ClrBitNow:&lt;br /&gt;
   cbi  PortB, 0    ; clear bit (if bit was set)&lt;br /&gt;
BitReady:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kürzer gehts mit folgender Variante. Zwischen der 2. und 3. Instruktion darf jedoch kein Interrupt erfolgen, sonst würde das SBIC schon den neuen Pegel des SBI auswerten (evtl. globale Interrupts ausschalten).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 cli                 ; disable global interrupts&lt;br /&gt;
 sbis   PinB,   0    ; skip if bit is set&lt;br /&gt;
 sbi    PortB,  0    ; otherwise set bit&lt;br /&gt;
 sbic   PinB,   0    ; skip if bit is cleared&lt;br /&gt;
 cbi    PortB,  0    ; otherwise clear bit&lt;br /&gt;
 sei                 ; enable global interrupts&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch kürzer gehts so:&amp;lt;br&amp;gt;&lt;br /&gt;
Die zweite Zeile mit dem Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; lädt die Bitmaske, in welcher die zu  den zu toggelnden Bits auf &#039;1&#039; gesetzt sind. In diesem Beispiel wird das dritte Bit invertiert. Der Vorteil dieser Methode ist neben der Kürze und Übersichtlichkeit auch die Möglichkeit, bis zu 8 Bit gleichzeitig zu toggeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in     R24, PORTE   ; Daten lesen&lt;br /&gt;
 ldi    R25, 0x04    ; Bitmaske laden, hier Bit#2&lt;br /&gt;
 eor    R24, R25     ; Exklusiv ODER&lt;br /&gt;
 out    PORTE, R24   ; Daten zurückschreiben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit gibt es, wenn man nur das 8. Bit kippen will:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in      r16, PORTB&lt;br /&gt;
 subi    r16, 0x80&lt;br /&gt;
 out     PORTB, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 PORTB ^= (1&amp;lt;&amp;lt;PB0);    /* XOR, Kurzschreibweise, PORTB = PORTB ^ (1&amp;lt;&amp;lt;PB0) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Neuere ATmegas ===&lt;br /&gt;
&lt;br /&gt;
Bei den neueren ATmegas (z.B. ATmega48) kann man IO-Pins direkt ohne den Umweg über Register togglen, indem man das entsprechende Bit im PINx-Register &#039;&#039;&#039;setzt&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbi PIND, 2       ; Bit 2 von Port D togglen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 8051er ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cpl bitadresse&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:8051]][[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=SDRAM-Timing&amp;diff=25290</id>
		<title>SDRAM-Timing</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=SDRAM-Timing&amp;diff=25290"/>
		<updated>2007-12-31T10:10:07Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Links ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung==&lt;br /&gt;
&lt;br /&gt;
SDRAM ist mittlerweile ein Standardbauteil im Bereich der Mikroprozessoren. Doch wie kann man prüfen, ob das Timing des Businterface eingehalten wird?&lt;br /&gt;
&lt;br /&gt;
==Timing synchroner Systeme==&lt;br /&gt;
&lt;br /&gt;
Jede synchrone Logikschaltung folgt einem grundlegenden Prinzip. Viele FlipFlops speichern Bits, mit denen Daten und Zustandsvariablen von State Machines kodiert sind. Zwischen den FlipFlops gibt es im einfachsten Fall eine direkte Verbindung, z.B. in einem Schieberegister. Wenn Daten logisch verarbeitet werden sollen, befindet sich zwischen FlipFlops eine logische Verknüpfung mehrerer Signale, die je nach Komplexität eine kurze oder lange Durchlaufverzögerung aufweist. Alle FlipFlops werden über ein spezielles Netzwerk mit einem Takt versorgt. Das Netzwerk sorgt dafür, daß der Takt von einer Quelle nahezu gleichzeitig bei allen FlipFlops ankommt (typisch +/-100ps bei modernen FPGAs). Das ist auch notwendig, darauf baut die synchrone Schaltungstechnik auf.&lt;br /&gt;
&lt;br /&gt;
Wie bestimmt man von so einem System die maximale Taktfrequenz? Indem man sich den zeitlichen Ablauf der Signale verdeutlicht und letztendlich in Zahlen ausdrückt.&lt;br /&gt;
Was passiert während einer Taktperiode? Mit dem gleichzeitigen Eintreffen der aktiven (meist steigenden) Taktflanke geben alle FlipFlops neue Daten aus, die am Eingang anliegen. Das sind die Daten, die in der vorherigen Taktperiode berechnet wurden. Das dauert eine kleine Weile. Wie lange ist von der Technologie des ICs abhängig (Bruchteile von Nanosekunden bis einige Dutzend Nanosekunden). Allgemein wird diese Zeit als Clock-to-Output-Time bezeichnet. Es ist die Zeit die ein FlipFlop braucht, um nach der Taktflanke neue Daten auszugeben. Danach müssen die Signale die kombinatorische Logik durchlaufen (wenn es welche gibt). Diese Zeit nennt man Propagation Delay Time (kombinatorische Durchlaufverzögerung). Wenn keine Logik zwischen den FlipFlops liegt kommt nur die reine Laufzeit auf den Verbindungsleitungen zum Tragen. Auf den ersten Blick scheint das komisch, schliesslich breiten sich elektrische Signale auf Leitungen mit ca. halber Lichtgeschwindigkeit aus, wie kann da die Laufzeit von ein paar &amp;amp;mu;m auf einem IC bzw. ein paar cm zwischen ICs eine Rolle spielen? Nun, wenn man sich die Taktraten moderner ICs vor Augen führt, dann erscheint die Lichtgeschwindigkeit doch schon eher moderat. Ein Signal legt pro Nanosekunde ca. 15..20cm auf einer Leiterplatte oder einem Kabel zurück. Wir werden noch sehen, daß das bisweilen langsam sein kann. (In einem FPGA bestehen die Verbindungen nicht aus einfachem Metall sondern sind konfigurierbare Schalter, welche aus Transistoren aufgebaut sind. Nicht selten ist Laufzeit auf den Leitungen genauso hoch wie die Verzögerung der reinen Logikfunktionen.) Doch nachdem unsere neuen Daten ausgegeben, logisch verknüpft wurden und die Leitungen hinter sich gelassen haben, müssen sie rechtzeitig vor der nächsten Taktflanke am Ziel ankommen, sprich am FlipFlop, welches die Daten erhalten soll. Und zwar nicht &amp;quot;fünf vor Zwölf&amp;quot;, sondern mindestens die sog. Setup-zeit vorher. Diese ist wieder von der IC-Technologie abhängig. Wenn all diese Dinge nacheinander passiert sind kann die nächste Taktflanke die neuen Daten in die FlipFlops übernehmen. Damit ist der Zyklus fast vollständig. Was noch fehlt ist die sog. Hold-time, also die Zeit, welche die Daten noch nach der Taktflanke stabil anliegen müssen. Bei den meisten ICs ist diese Zeit 0ns, aber einige ICs brauchen ein paar ns. Das Datenblatt sagt hier was notwendig ist.&lt;br /&gt;
&lt;br /&gt;
Aus der eben dargestellten Erklärung können wir die minimale Taktperiode (=maximale Taktfrequenz) berechnen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{CLK}&amp;gt;=T_{C2O}+T_{PD}+T_{RT}+T_{S}&amp;lt;/math&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{CLK}&amp;lt;/math&amp;gt;   || Periodendauer des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; 	|| Clock to Output Time (Ausgabezeit des FlipFlops)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{PD}&amp;lt;/math&amp;gt;    || Propagation Delay Time (Durchlaufzeit kombinatorischer Logik)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT}&amp;lt;/math&amp;gt;    || Run Time (Laufzeit auf Verbindungsleitungen)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{S}&amp;lt;/math&amp;gt;     || Setup Time (Stabilisierungszeit für Daten)&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{H}&amp;lt;/math&amp;gt;     || Hold Time (Haltezeit für Daten)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für die Hold Time kann folgende Beziehung aufgestellt werden&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_H &amp;lt;= T_{C2O}+T_{PD}+T_{RT}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie man sieht macht die Hold Time wahrscheinlich selten Probleme, zumal sie oft Null ist. Doch Vorsicht! Selten heißt nicht niemals! Also besser nachrechnen und messen!&lt;br /&gt;
&lt;br /&gt;
Die Differenz aus minimaler Taktperiode und der realen Taktperiode, mit der die Schaltung betrieben wird, steht als Timingreserve zur Verfügung, (englisch Slack). Wieviel Reserve eine Schaltung braucht ist bisweilen gar nicht so einfach zu sagen. Ein AVR mit 20MHz (50ns) ist mit 5ns sehr gut bedient (55ns, 18 MHz). Ein DDR-RAM mit 200MHz hat gerade mal 5ns Periodendauer, da sind 500ps schon 10%!&lt;br /&gt;
&lt;br /&gt;
==SDRAM Timing bei sternförmiger Taktverteilung==&lt;br /&gt;
&lt;br /&gt;
Aufbauend auf dem oben beschriebenen Prinzip kann man nun das Zusammenspiel zwischen Mikrocontroller und SDRAM bei sternförmiger Taktverteilung beschreiben. Dabei wird von einer zentralen Taktquelle (meist ein Quarz) über einen Takttreiber, welcher ein Eingangssignal auf mehrere Ausgänge sehr gleichmäßig verteilt, sternförmig an alle Teilnehmer des synchronen Busses verteilt. Die einzelnen Stränge vom Takttreiber zu den einzelnen Verbrauchern sind dabei gleich lang und haben die gleiche Ausbreitungsgeschwindigkeit. Damit kommt das Taktsignal sehr synchron bei allen Verbrauchern an, +/-500ps und weniger sind hier recht problemlos machbar. Zu beachten ist, daß die Ausbreitungsgeschwindigkeit elektrischer Signale auf Leiterbahnen unterschiedlich sein kann! Aussenlagen sind schneller als Innenlagen in Multilayerplatinen (ca. 20cm/ns zu 15 cm/ns). Paßt man da nicht auf haben gleich lange Taktleitungen unterschiedliche Laufzeiten! Doch das nur am Rande.&lt;br /&gt;
&lt;br /&gt;
Mit dem beschriebenen Aufbau kann man nun das Timing analysieren. Dabei muß man zwei wesentliche Fälle unterscheiden.&lt;br /&gt;
&lt;br /&gt;
===Schreibzugriff===&lt;br /&gt;
&lt;br /&gt;
Die Daten laufen vom Mikrocontroller zum SDRAM. Das gilt immer für den Adress- und Steuerbus sowie den Datenbus bei einem Schreibvorgang.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;math&amp;gt;T_{CLK}&amp;gt;=T_{C2O}+T_{RT}+T_{S}&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;T_H &amp;lt;= T_{C2O}+T_{RT}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{Clk}&amp;lt;/math&amp;gt; || Periodendauer des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; || maximale Ausgabezeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT}&amp;lt;/math&amp;gt;  || Laufzeit auf Verbindungsleitungen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{S}&amp;lt;/math&amp;gt;   || minimale Stabilisierungszeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt;     || minimale Haltezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Lesezugriff===&lt;br /&gt;
&lt;br /&gt;
Die Daten laufen vom SDRAM zum Mikrocontroller. Das gilt für den Datenbus bei einem Lesevorgang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{CLK}&amp;gt;=T_{C2O}+T_{RT}+T_{S}&amp;lt;/math&amp;gt;&lt;br /&gt;
&amp;lt;math&amp;gt;T_H &amp;lt;= T_{C2O}+T_{RT}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{CLK}&amp;lt;/math&amp;gt; || Periodendauer des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; || maximale Ausgabezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT}&amp;lt;/math&amp;gt;  || Laufzeit auf Verbindungsleitungen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{S}&amp;lt;/math&amp;gt; 	 || minimale Stabilisierungszeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt;  	 || minimale Haltezeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Das war&#039;s eigentlich schon. Für beide Fälle müssen beide Formeln für &amp;lt;math&amp;gt;T_{CLK}&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt; erfüllt sein. &amp;lt;math&amp;gt;T_{PD}&amp;lt;/math&amp;gt; ist immer Null, da hier keine weitere Logik zwischen SDRAM und Mikrocontroller liegt, nur Leiterbahnen.&lt;br /&gt;
&lt;br /&gt;
==SDRAM Timing bei Taktverteilung vom Mikrocontroller==&lt;br /&gt;
&lt;br /&gt;
Etwas kniffliger wird die Angelegenheit wenn der Takt vom Mikrocontroller geliefert wird. Sämtliche Timings im Datenblatt beziehen sich auf das Taktsignal direkt am Gehäuse. Wenn nun aber eine etwas längere Taktleitung gebraucht wird, verschieben sich die Zeiten. Aber wie? Das Wort zur Beschreibung von verschobenen Taktflanken in synchronen Systemen heißt Skew. Im Idealfall ist er Null, real aber leider einiges größer.&lt;br /&gt;
Betrachten wir zunächst die Datenrichtung vom Mikrocontroller zum SDRAM. Takt und Daten bewegen sich in die gleiche Richtung. Was hier scheinbar gut klingt kann böse ins Auge gehen. Wie? Nehmen wir an, die Taktleitung ist sehr viel länger als die Datenleitungen. Dann kann es bei schnellen ICs passieren, daß die Daten eher am nächsten FlipFlop sind (hier der SDRAM) als der Takt! Wenn dann der Takt verspätet eintrifft werden nicht die Daten des vorherigen Zyklus eingetaktet sondern die neuen! Es scheint dann so, als ob ein Taktzyklus fehlt! Demzufolge muß der Takt immer als erster ankommen. Wobei der Takt meist den Vorteil hat, daß die Daten noch von den FlipFlops ausgegeben werden müssen. Mathematisch heißt das.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;=T_{RT-DATA}+T_{C2O}+T_H&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;/math&amp;gt;  || Laufzeit des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-DATA}&amp;lt;/math&amp;gt; || Laufzeit der Daten&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt;     || minimale Ausgabezeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt;         || minimale Haltezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es ist also ratsam, die Taktleitung so kurz wie möglich zu halten. Auf grösseren Boards mit vielen SDRAMs und hohen Taktfrequenzen ist das manchmal schwierig bis unmöglich. Abhilfe schafft  hier ein sog. PLL-Takttreiber. Dieser hat zusätzlich zum normalen Takttreiber eine PLL eingebaut, mit deren Hilfe Taktsignale &amp;quot;gespiegelt&amp;quot; werden können, d.h. Leitungslängen werden komplett kompensiert und an allen Verbrauchern liegt der Takt mit sehr geringer Phasenverschiebung an. Damit ist wieder ein System mit sternförmiger Taktverteilung entstanden und die Betrachtung vereinfacht sich. In FPGAs von Xilinx verwendet man DLLs (&#039;&#039;&#039;D&#039;&#039;&#039;elay &#039;&#039;&#039;L&#039;&#039;&#039;ocked &#039;&#039;&#039;L&#039;&#039;&#039;oop) bzw. in den neueren Familien DCMs (&#039;&#039;&#039;D&#039;&#039;&#039;igital &#039;&#039;&#039;C&#039;&#039;&#039;lock &#039;&#039;&#039;M&#039;&#039;&#039;anager). Mit diesen Komponenten ist es möglich, den Takt im FPGA sowie am SDRAM in der Phase auszurichten, genauso wie es die PLL-Takttreiber auch machen. Siehe [[#Links | Links]].&lt;br /&gt;
&lt;br /&gt;
===Schreibzugriff===&lt;br /&gt;
&lt;br /&gt;
Doch zurück zu unserem Takt, welcher eine gewisse Laufzeit bis zum SDRAM braucht. Wenn der später am Ziel (SDRAM) ankommt, dann haben die Daten auch ein klein wenig mehr Zeit, sich bis zum Ziel &amp;quot;durchzuschlagen&amp;quot;. Mathematisch heißt das&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{CLK}&amp;gt;=T_{C2O}+T_{RT}+T_{S}- T_{RT-CLK}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{CLK}&amp;lt;/math&amp;gt; || Periodendauer des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; || maximale Ausgabezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT}&amp;lt;/math&amp;gt;  || Laufzeit auf Verbindungsleitungen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{S}&amp;lt;/math&amp;gt;   || minimale Stabilisierungszeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;/math&amp;gt; || Laufzeit des Taktes&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wer jetzt aber meint mit einer großen Laufzeit des Taktes die maximale Taktfrequenz erhöhen zu können irrt! Was hier scheinbar höhere Taktfrequenzen zuläßt, ist bei der Hold Time kontraproduktiv. Ausserdem darf man den Lesezugriff nicht vergessen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T_H  &amp;lt;= T_{RT-DATA}+T_{C2O}- T_{RT-CLK}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;/math&amp;gt;  || Laufzeit des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-DATA}&amp;lt;/math&amp;gt; || Laufzeit der Daten&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; 	 || minimale Ausgabezeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt; 		 || minimale Haltezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Lesezugriff===&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt müssen wir noch die Datenrichtung vom SDRAM zum Mikrocontroller prüfen. Wie wirkt sich hier eine Laufzeit des Taktes aus? Nun, wenn der Takt später am SDRAM ankommt werden die Lesedaten später &amp;quot;auf die Reise gehen&amp;quot;. Das bedeutet, daß sie später am Mikrocontroller ankommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;T_{CLK}&amp;gt;=T_{C2O}+T_{RT}+T_{S}+T_{RT-CLK}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{CLK}&amp;lt;/math&amp;gt; || Periodendauer des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; || maximale Ausgabezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT}&amp;lt;/math&amp;gt; 	|| Laufzeit auf Verbindungsleitungen&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{S}&amp;lt;/math&amp;gt; 	|| minimale Stabilisierungszeit der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;/math&amp;gt; || Laufzeit des Taktes vom Mikrocontroller zum SDRAM&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Immerhin ändern sich damit die Daten nicht so schnell wie im Normalfall, so daß die Hold Time hier keine Probleme macht, da Takt und Daten entgegengesetzt laufen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; T_H  &amp;lt;= T_{RT-DATA}+T_{C2O}+ T_{RT-CLK}&amp;lt;/math&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-CLK}&amp;lt;/math&amp;gt;  || Laufzeit des Taktes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{RT-DATA}&amp;lt;/math&amp;gt; || Laufzeit der Daten&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_{C2O}&amp;lt;/math&amp;gt; 	 || minimale Ausgabezeit der Daten am SDRAM, Datenblatt&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;math&amp;gt;T_H&amp;lt;/math&amp;gt; 		 || minimale Hold Time der Daten am Mikrocontroller, Datenblatt&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Zusammenfassung==&lt;br /&gt;
&lt;br /&gt;
*Wenn Takt und Daten in die gleiche Richtung laufen können Probleme mit der Hold Time auftreten&lt;br /&gt;
*Wenn Takt und Daten in die entgegengesetzte Richtung laufen können Probleme mit der Setup Time auftreten&lt;br /&gt;
*Eine sternförmige Taktverteilung ist einfacher zu analysieren&lt;br /&gt;
*Bei langen Taktleitungen und vielen Taktempfängern sind PLL-Takttreiber hilfreich&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.micron.com/products/dram/ DRAM bei Micron]&lt;br /&gt;
* [http://www.micron.com/products/dram/sdram/technotes Application Notes von Micron zu SDRAMs]&lt;br /&gt;
* [http://www.hynix.com/datasheet/Timing_Device/DDR_DeviceOperation.pdf Application Notes von Hynix]&lt;br /&gt;
* [http://www.samsung.com/global/business/semiconductor/products/dram/Products_DRAM.html DRAM bei Samsung]&lt;br /&gt;
* [http://www.samsung.com/global/business/semiconductor/products/dram/DRAMApplicationNote.html Application Notes von Samsung]&lt;br /&gt;
* [http://www.xilinx.com/bvdocs/appnotes/xapp174.pdf Application Note von Xilinx: &amp;quot;Using Delay-Locked Loops in Spartan-II FPGAs&amp;quot;]&lt;br /&gt;
* [http://direct.xilinx.com/bvdocs/appnotes/xapp134.pdf Application Note von Xilinx: &amp;quot;SDRAM Controller in Spartan-II FPGAs&amp;quot;]&lt;br /&gt;
* [http://direct.xilinx.com/bvdocs/appnotes/xapp454.pdf Application Note von Xilinx: &amp;quot;DDR2 SDRAM Memory Interface for Spartan-3 FPGAs&amp;quot;]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/64305#new Diskussion zum Thema SDRAM + FPGA]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Bascom_AVR&amp;diff=25289</id>
		<title>Bascom AVR</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Bascom_AVR&amp;diff=25289"/>
		<updated>2007-12-30T23:10:47Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Weblinks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
Bascom AVR ist ein kommerzieller [[Basic]]-Compiler für die [[AVR]]s. Die kostenlose Demoversion kann Code bis zu einer Größe von 2kB erzeugen, womit man z.B. den Speicher des AT90S2313 vollständig ausnutzen kann. Bascom AVR beinhaltet sehr viele fertige Funktionen mit denen das Ansteuern von externen Bauteilen (wie Servos, LCDs usw.) sehr einfach zu realisieren ist.&lt;br /&gt;
&lt;br /&gt;
Problematisch bei Bascom AVR ist leider die Festlegung der Werte für den Software- und Hardwarestack sowie das Festlegen der Framesize. Diese Werte beeinflussen die Variablen und das komplette Programm. Bei großen Programmen kann das zum Verhängnis werden und negative Auswirkungen haben.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://www.mcselec.com/bascom-avr.htm Seite des Herstellers]&lt;br /&gt;
* [http://www.mcselec.com/index.php?option=com_docman&amp;amp;task=doc_download&amp;amp;gid=140&amp;amp;Itemid=54 Das Handbuch aller Befehle, ein absolutes &#039;&#039;&#039;Muss&#039;&#039;&#039; für jeden BASCOMer!]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Bascom_AVR&amp;diff=25288</id>
		<title>Bascom AVR</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Bascom_AVR&amp;diff=25288"/>
		<updated>2007-12-30T23:10:15Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Weblinks */ Link auf Handbuch hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
&lt;br /&gt;
Bascom AVR ist ein kommerzieller [[Basic]]-Compiler für die [[AVR]]s. Die kostenlose Demoversion kann Code bis zu einer Größe von 2kB erzeugen, womit man z.B. den Speicher des AT90S2313 vollständig ausnutzen kann. Bascom AVR beinhaltet sehr viele fertige Funktionen mit denen das Ansteuern von externen Bauteilen (wie Servos, LCDs usw.) sehr einfach zu realisieren ist.&lt;br /&gt;
&lt;br /&gt;
Problematisch bei Bascom AVR ist leider die Festlegung der Werte für den Software- und Hardwarestack sowie das Festlegen der Framesize. Diese Werte beeinflussen die Variablen und das komplette Programm. Bei großen Programmen kann das zum Verhängnis werden und negative Auswirkungen haben.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://www.mcselec.com/bascom-avr.htm Seite des Herstellers]&lt;br /&gt;
* [http://www.mcselec.com/index.php?option=com_docman&amp;amp;task=doc_download&amp;amp;gid=140&amp;amp;Itemid=54 Das Handbuch aller Befehle, ein absolues &#039;&#039;&#039;Muss&#039;&#039;&#039; für jeden BASCOMer!]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Festkommaarithmetik&amp;diff=25274</id>
		<title>Festkommaarithmetik</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Festkommaarithmetik&amp;diff=25274"/>
		<updated>2007-12-29T16:33:05Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Kauderwelsch wieder entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Das Problem ==&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Problem ist die Anzeige von Messwerten per UART/RS232 oder LCD. Die Messwerte werden praktisch immer von [[AD-Wandler]]n oder anderen Sensoren in digitaler Form geliefert. Doch wie wandelt man diese in eine &amp;quot;normale&amp;quot; Anzeige wie z.B. 4,56V um?&lt;br /&gt;
&lt;br /&gt;
Der einfachste Ansatz ist der Einsatz von Gleitkommazahlen. Dabei gilt nahezu immer die Formel&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Eingangsspannung = \frac{ADC \hbox{-} Wert \cdot Referenzspannung}{ 2^{Bitbreite_{ADC}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den so errechneten Wert der Eingangsspannung kann man einfach per C-Funktion&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
sprintf(mein_string, &amp;quot;%2.4f&amp;quot;, Eingangsspannung);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in einen String umwandeln und dann auf ein LCD oder den UART ausgeben.&lt;br /&gt;
&lt;br /&gt;
Soweit so gut. Das Problem ist &amp;quot;nur&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* Das Rechnen mit Gleitkommazahlen ist auf den meisten Mikrocontrollern mit recht viel Rechenzeit sowie Programmspeicher verbunden, da diese keine Befehle zur direkten Verarbeitung von Gleitkommazahlen haben. Alle Rechnungen werden mit Hilfe von Bibliotheksfunktionen nachgebildet.&lt;br /&gt;
* Die Ausgabe von Gleitkommazahlen per sprintf und Konsorten benötigt ebenfalls sehr viel Programmspeicher, da diese Funktionen sehr mächtig und damit umfangreich sind&lt;br /&gt;
&lt;br /&gt;
== Die Lösung ==&lt;br /&gt;
&lt;br /&gt;
Die allerwenigsten Anwendungen benötigen die volle Leistung von Gleitkommazahlen (Dynamikbereich). Wesentlich sinnvoller ist die Anwendung von Festkommazahlen. Diese sind normale Integerzahlen (ganzzahlig ohne Kommastellen), allerdings mit &amp;quot;gedachten&amp;quot; Kommastellen. Wie geht das? Ganz einfach. Anstatt 10,45 kann man auch 1045 schreiben und die beiden letzten Stellen als Nachkommastellen betrachten. 1045 kann man in einer ganz normalen 16 Bit Integervariable speichern. Und auch damit rechnen!&lt;br /&gt;
&lt;br /&gt;
Anstatt nun in Gleitkomma zu schreiben und zu rechnen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U = \frac{756 \cdot 5,0}{1024}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird geschrieben&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;U = \frac{756 \cdot 5000}{1024}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Referenzspannung wird in 1/1000 V (mV) ausgedrückt und damit gerechnet. Das Ergebnis ist eine Spannung in mV. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Wichtig&amp;lt;/B&amp;gt; ist dabei, daß&lt;br /&gt;
&lt;br /&gt;
* erst alle Multiplikationen und dann erst die Divisionen durchgeführt werden, um Rundungsfehler zu minimieren&lt;br /&gt;
* in sämtlichen Zwischenergebnissen keine arithmetischen Überläufe auftreten, also genügend grosse Variablen benutzen (16/32/64 Bit Integer)&lt;br /&gt;
&lt;br /&gt;
Doch wie wird nun diese Zahl in einen darstellbaren String umgewandelt? Im einfachsten Fall durch Verwendung der C-Funktion itoa (Integer to ASCII). Diese ist auf vielen Mikrocontrollern als C-Bibliothek verfügbar. Wenn dies jedoch nicht so sein sollte muss man sie selber schreiben. Und da wir hier etwas lernen wollen, werden wir das auch tun.&lt;br /&gt;
&lt;br /&gt;
== ITOA selbst gemacht ==&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert nun die Umwandlung einer Zahl in einen String? Ganz einfach. Für jede Stelle der Dezimalzahl muss ein ASCII-Zeichen erzeugt werden. Wenn z.B. die Zahl 28943 in einen String gewandelt werden soll muss am Ende der String die ASCII-Codes 0x32, 0x38, 0x39, 0x34, 0x33 und 0x00 (Stringabschlusszeichen, Stringterminator) enthalten. Wie man bei genauem Hinsehen sieht, besteht der ASCII-Code einer Zahl zwischen 0..9 immer aus 0x30 + Zahl. Das ist einfach. Und wie kommt man nun an die einzelnen Ziffern? Dazu wird eine MODULO Operation durchgeführt. Diese liefert den Rest einer ganzzahligen Division.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
28943 MOD 10 =       3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun gehts an die nächste Stelle. Dazu wird die Zahl einfach durch 10 dividiert&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
28943 / 10 = 2894&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Und nun das Spiel von vorn.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2894 MOD 10 =        4&lt;br /&gt;
2894 / 10   = 289&lt;br /&gt;
289 MOD 10  =        9&lt;br /&gt;
289 / 10    = 28&lt;br /&gt;
28 MOD 10   =        8&lt;br /&gt;
28 / 10     = 2&lt;br /&gt;
2 MOD 10    =        2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das wars eigentlich schon. Beachtet werden muss nur, dass bei dieser Methode die einzelnen Stellen in umgekehrter Reihenfolge entstehen: Die höchstwertigen Stellen kommen erst zum Schluss.&lt;br /&gt;
&lt;br /&gt;
Hier ist nun unsere erste einfache Funktion, um eine vorzeichenlose 32 Bit Zahl in einen String umzuwandeln. Diese kann maximal 10 Dezimalstellen haben (0..4294967295), also wird ein Speicher für 11 Bytes benötigt (letztes Byte für den Stringterminator).  Bei dem Verfahren wird die Zahl rückwärts berechnet. Das muss bei der Ablage im Speicher berücksichtigt werden. Im Sinne der Verständlichkeit wurde bewusst auf Optimierungen und kompakt/kryptische Schreibweisen verzichtet. Der Syntax ist Standard-C und somit auf jedem Compiler nutzbar.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Funktion zur Umwandlung einer vorzeichenlosen 32 Bit Zahl in einen String&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void my_uitoa(uint32_t zahl, char* string) {&lt;br /&gt;
  int8_t i;                             // schleifenzähler&lt;br /&gt;
&lt;br /&gt;
  string[10]=&#039;\0&#039;;                       // String Terminator&lt;br /&gt;
  for(i=9; i&amp;gt;=0; i--) {&lt;br /&gt;
    string[i]=(zahl % 10) +&#039;0&#039;;         // Modulo rechnen, dann den ASCII-Code von &#039;0&#039; addieren&lt;br /&gt;
    zahl /= 10;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion gibt auch führende Nullen aus. Das ist erstmal OK, denn so wissen wir immer wo unser gedachtes Komma ist. Bei der Ausgabe können die führenden Nullen unterdrückt werden. Wie das geht, wird weiter unten beschrieben.&lt;br /&gt;
&lt;br /&gt;
Benötigt man neben der Ausgabe für vorzeichenlose (unsigned) Werte auch noch eine Funktion für vorzeichenbehaftete Werte, so ist auch dieses keine Hexerei. Dazu verwenden wir ein zusätzliches Byte im String um das Vorzeichen zu speichern. Der String muss nun also mindestens 12 Bytes Speicherplatz zur Verfügung stellen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Funktion zur Umwandlung einer vorzeichenbehafteten 32 Bit Zahl in einen String&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void my_itoa(int32_t zahl, char* string) {&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  string[11]=&#039;\0&#039;;                  // String Terminator&lt;br /&gt;
  if( zahl &amp;lt; 0 ) {                  // ist die Zahl negativ?&lt;br /&gt;
    string[0] = &#039;-&#039;;              &lt;br /&gt;
    zahl = -zahl;&lt;br /&gt;
  }&lt;br /&gt;
  else string[0] = &#039; &#039;;             // Zahl ist positiv&lt;br /&gt;
&lt;br /&gt;
  for(i=10; i&amp;gt;=1; i--) {&lt;br /&gt;
    string[i]=(zahl % 10) +&#039;0&#039;;     // Modulo rechnen, dann den ASCII-Code von &#039;0&#039; addieren&lt;br /&gt;
    zahl /= 10;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Digitaler Temperatursensor ===&lt;br /&gt;
&lt;br /&gt;
Anstatt eines ADC-Wertes werden oft auch Digitalwerte von Temperatursensoren verarbeitet. Der Weg ist hier identisch. Allerdings hat man keine Referenzspannung oder Referenztemperatur. Das ist aber kein Beinbruch. Z.B. der LM74 hat eine Auflösung von 1/16°C = 0,0625°C. Um das Messergebnis ohne Verlust von Auflösung auszugeben könnte man als erstes den Digitalwert auf 1/100 °C umrechnen. Das geschieht mit der Multiplikation mit 6,25. Doch Stop, das ist ja schon wieder ne Gleitkommazahl. Doch kein Problem, wir wissen ja wie wir das Problem lösen. Wir schieben das Komma um zwei Stellen nach rechts und multiplizieren mit 625 und wissen, dass das Ergbniss nun in 1/10000°C vorliegt. Über den physikalischen Sinn dieser Auflösung müssen wir nicht nachdenken, wichtig ist für uns nur, dass jetzt die Zahl einfach per itoa() umwandelbar ist. Allgemein kann man folgenden Ablauf zur Berechnung des Korrekturfaktors angeben&lt;br /&gt;
&lt;br /&gt;
* Den Korrekturfaktur &amp;lt;B&amp;gt;K&amp;lt;/B&amp;gt; mit vollen Kommastellen berechnen, dabei ist die neue Auflösung sinnvollerweise eine Dezimalzahl ( 0,1; 0,001 etc.) und kleiner als die alte Auflösung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;K  = \frac{alte~Aufloesung}{neue~Aufloesung}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Den berechneten Korrekturfaktor solange mit 10 multiplizieren bis alle Nachkommastellen verschwunden sind bzw. durch Rundung ein akzeptabler Fehler entsteht. Für jede Multiplikation des Faktors mit 10 muss die neue Auflösung durch 10 dividiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Temperatur = Sensorwert \cdot K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Beispiel:&amp;lt;/B&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alte Auflösung: 1/32°C&amp;lt;BR&amp;gt;&lt;br /&gt;
Neue Auflösung: 1/100°C&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;K = \frac{\frac{1}{32}}{\frac{1}{100}} = 3,125&amp;lt;/math&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Der Korrekturfaktor 3,125 kann zweimal mit 10 multipliziert werden und dann auf 312 gerundet werden, das entspricht einem Rundungsfehler von gerade mal 1/624 = 0,16% ! Die neue Auflösung beträgt 1/10000°C. Wenn der Sensor nur 7 Bit Werte liefert kann das Ergebnis in einer 16 Bit Variablen gespeichert werden, darüber hinaus ist eine 32 Bit Variable notwendig.&lt;br /&gt;
&lt;br /&gt;
=== ADC allgemein ===&lt;br /&gt;
&lt;br /&gt;
Wenn man das obige ADC-Beispiel allgemein beschreiben will, dann gilt folgender Ablauf&lt;br /&gt;
&lt;br /&gt;
* Korrekturfaktor mit vollen Kommastellen berechnen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;alte Aufloesung = \frac{Referenzspannung}{2^{Bitbreite_{ADC}}}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;K  = \frac{alte~Aufloesung}{neue~Aufloesung}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Den berechneten Korrekturfaktor K solange mit 10 multiplizieren bis alle Nachkommastellen verschwunden sind bzw. durch Rundung ein akzeptabler Fehler entsteht. Für jede Multiplikation des Faktors mit 10 muss die neue Auflösung durch 10 dividiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Eingangsspannung = ADC \hbox{-} Wert \cdot K&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;B&amp;gt;Beispiel:&amp;lt;/B&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Referenzspannung : 5V&amp;lt;BR&amp;gt;&lt;br /&gt;
ADC Bitbreite : 10&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;alte Aufloesung = \frac{5V}{2^{10}} = \frac{5}{1024} = 0,0048828125\,V&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
neue Auflösung:  0,001 V = 1mV&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;K = \frac{0,0048828125 V}{0,001V} = 4,8828125&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Korrekturfaktor 4,8828125 wird zweimal mit 10 multipliziert und dann auf 488 gerundet, das entspricht einem Rundungsfehler von gerade mal 0,05% ! Die neue Auflösung ist 0,00001 V = 10uV. Das Ergebnis muss in einer 32 Bit Variablen gespeichert werden, denn der grösste Messwert ergibt 1023*488 = 499224. Der Vorteil dieses allgemeinen Ansatzes ist vor allem, daß nur eine Multiplikation benötigt wird, im Gegensatz zu unserem allerersten Beispiel, welches eine Multiplikation und eine Division benötigt. Das spart einiges an Rechenzeit und Programmspeicherplatz.&lt;br /&gt;
&lt;br /&gt;
== Ergebnis runden ==&lt;br /&gt;
&lt;br /&gt;
Nach der Umrechung des ADC/Sensor-Wertes und dem Aufruf von my_uitoa() kann man sehr einfach eine Rundung durchführen. Dazu muss nur die erste Stelle, welche durch Rundung wegfallen soll, geprüft werden. Ist sie kleiner als der ASCII-Code von &#039;5&#039; (0x35) dann muss abgerundet werden, sprich alles bleibt wie es ist. Im anderen Fall muss aufgerundet werden, was bedeutet dass die letzte angezeigte Dezimalstelle um eins erhöht werden muss. Doch aufgepasst! Wenn z.B. in unserem String die Zahl 1995 steht und die letzte Stelle durch Rundung wegfallen soll, kommte es zum Übertrag. Die letzte 9 wird zur 0 + Übertrag. Die nächste linksstehende Stelle muss erhöht werden. Das ist &amp;quot;dummerweise&amp;quot; auch eine 9, also wieder ein Übertrag. Die letzte Ziffer ist 1, die wird nur auf zwei erhöht und der Übertrag endet.&lt;br /&gt;
&lt;br /&gt;
Basierend auf unserer Funktion my_uitoa() soll hier eine einfache Rundungsfunktion gezeigt werden. Auch diese arbeitet mit einem 11 Byte String, welcher vorher durch my_uitoa erzeugt wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Funktion zur Rundung einer vorzeichenlosen 32 Bit Zahl im Stringformat&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
&lt;br /&gt;
char* string:  Zeiger auf String, welcher mit my_uitoa() erzeugt wurde&lt;br /&gt;
uint8_t digit: Offset im String, zeigt auf die Stelle welche zur Rundung ausgewertet werden soll&lt;br /&gt;
               gültiger Wertebereich ist 1..9 !&lt;br /&gt;
               Der Offset von 1 zeigt auf die zweite Stelle von links&lt;br /&gt;
               Der Offset von 9 zeigt auf die letzte Stelle von links&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void my_round(char* string, uint8_t digit) {&lt;br /&gt;
  uint8_t i;&lt;br /&gt;
&lt;br /&gt;
  if (string[digit]&amp;gt;=&#039;5&#039;) {         // Aufrunden?&lt;br /&gt;
    for(i=(digit-1); i&amp;gt;=0; i--) {&lt;br /&gt;
      string[i] += 1;               // Aufrunden&lt;br /&gt;
      if (string[i]&amp;lt;=&#039;9&#039;)&lt;br /&gt;
        break;                      // kein Übertrag, schleife verlassen&lt;br /&gt;
      else&lt;br /&gt;
        string[i]=&#039;0&#039;;              // Übertrag und Überlauf&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  for(i=digit; i&amp;lt;10; i++) string[i] =&#039;0&#039;;   // gerundete Stellen auf Null setzen&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion ist auch auf einen String anwendbar, welcher mit my_itoa() erzeugt wurde (also vorzeichenbehaftete Zahlen). Allerdings muss der Funktion ein Pointer auf das zweite Byte (char) übergeben werden, da im ersten das Vorzeichen gespeichert ist. Das geschieht am einfachsten so.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
my_round(my_sting+1, 5);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Ergebnis ausgeben ==&lt;br /&gt;
&lt;br /&gt;
Nun haben wir unseren Messwert in eine Zahl mit der richtigen Einheit umgewandelt und gerundet. Jetzt folgt der letzte Schritt, die Ausgabe auf einen UART oder LCD. Dazu ist es meist wünschenswert führende Nullen nicht anzuzeigen. Also anstatt 0095,89 will man 95,89 ausgeben. Bei negativen Zahlen kommt noch das Vorzeichen hinzu.&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe auf LCD (HD44780 &amp;amp; CO) ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wollen wir unsere Zahl auf einem LCD ausgeben. Dazu braucht man die entsprechende Funktion lcd_data(), wie sie z.B. im [[AVR-GCC-Tutorial#LCD_Ansteuerung]] zu finden ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Funktion zur Anzeige einer 32 Bit Zahl im Stringformat&lt;br /&gt;
auf einem LCD mit HD44780 Controller&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
&lt;br /&gt;
char* string  : Zeiger auf String, welcher mit my_itoa() erzeugt wurde&lt;br /&gt;
uint8_t start : Offset im String, ab der die Zahl ausgegeben werden soll,&lt;br /&gt;
                das ist notwenig wenn Zahlen mit begrenztem Zahlenbereich&lt;br /&gt;
                ausgegeben werden sollen&lt;br /&gt;
                Vorzeichenlose Zahlen      : 0..10&lt;br /&gt;
                Vorzeichenbehaftete zahlen : 1..11&lt;br /&gt;
uint8_t komma : Offset im String, zeigt auf die Stelle an welcher das virtuelle&lt;br /&gt;
                Komma steht (erste Nachkommastelle)&lt;br /&gt;
                komma muss immer grösser oder gleich start sein !&lt;br /&gt;
&lt;br /&gt;
uint8_t frac  : Anzahl der Nachkommastellen&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void my_print_LCD(char* string, uint8_t start, uint8_t komma, uint8_t frac) {&lt;br /&gt;
&lt;br /&gt;
  uint8_t i;            // Zähler&lt;br /&gt;
  uint8_t flag=0;       // Merker für führende Nullen&lt;br /&gt;
&lt;br /&gt;
  // Vorzeichen ausgeben  &lt;br /&gt;
  if (string[0]==&#039;-&#039;) lcd_data(&#039;-&#039;); else lcd_data(&#039; &#039;);&lt;br /&gt;
&lt;br /&gt;
  // Vorkommastellen ohne führende Nullen ausgeben&lt;br /&gt;
  for(i=start; i&amp;lt;komma; i++) {&lt;br /&gt;
    if (flag==1 || string[i]!=&#039;0&#039;) {&lt;br /&gt;
      lcd_data(string[i]);&lt;br /&gt;
      flag = 1;&lt;br /&gt;
    }&lt;br /&gt;
    else lcd_data(&#039; &#039;);         // Leerzeichen&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  lcd_data(&#039;,&#039;);                // Komma ausgeben&lt;br /&gt;
&lt;br /&gt;
  // Nachkommastellen ausgeben&lt;br /&gt;
  for(; i&amp;lt;(komma+frac); i++) lcd_data(string[i]);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion gibt zunächst das Vorzeichen aus, das ist einfach. Danach werden die Vorkommastellen ausgegeben. Aber es wird nur dann ein Zeichen ausgegeben, wenn vorher schon mal eins ausgegeben wurde (flag ==1) oder das aktuelle Zeichen keine &#039;0&#039; ist. Danach werden ganz normal die Nachkommastellen ausgegeben, hier ist eine Unterdrückung führender Nullen sogar mathematisch falsch!&lt;br /&gt;
&lt;br /&gt;
Genutzt wird die Funktion beispielsweise so, um dreistellige Zahlen bis 999 mit zwei Nachkommastellen auszugeben. Die Zahl selber hat sechs Nachkommastellen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
char my_string[12]=&amp;quot;-0034567891\0&amp;quot;;&lt;br /&gt;
my_print_LCD(my_string, 2, 5, 2);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf dem LCD erscheint dann &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
- 34.56&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion kann auch verwendet werden, um vorzeichenlose Zahlen auszugeben, welche mit my_uitoa() erzeugt wurden. Da an der ersten Stelle nie ein &#039;-&#039; steht, wird auch nie ein &#039;-&#039; ausgegeben.&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe auf UART ===&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe auf einen UART ist nahezu identisch. Auch hier braucht man eine Funktion putc(), welche ein einzelnes Zeichen auf den UART schreiben kann wie z.B. im [[AVR-Tutorial:UART:GCC]] beschrieben ist. Der Unterschied zur LCD-Ausgabe besteht darin, dass zwischen dem Vorzeichen und der Zahl keinerlei Leerzeichen eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;lt;stdint.h&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
&lt;br /&gt;
Funktion zur Ausgabe einer 32 Bit Zahl im Stringformat&lt;br /&gt;
auf den UART&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
&lt;br /&gt;
char* string  : Zeiger auf String, welcher mit my_itoa() erzeugt wurde&lt;br /&gt;
uint8_t start : Offset im String, ab der die Zahl ausgegeben werden soll,&lt;br /&gt;
                das ist notwenig wenn Zahlen mit begrenztem Zahlenbereich&lt;br /&gt;
                ausgegeben werden sollen&lt;br /&gt;
                Vorzeichenlose Zahlen      : 0..10&lt;br /&gt;
                Vorzeichenbehaftete zahlen : 1..11&lt;br /&gt;
uint8_t komma : Offset im String, zeigt auf die Stelle an welcher das virtuelle&lt;br /&gt;
                Komma steht (erste Nachkommastelle);&lt;br /&gt;
                komma muss immer grösser oder gleich start sein !&lt;br /&gt;
&lt;br /&gt;
uint8_t frac  : Anzahl der Nachkommastellen&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
void my_print_UART(char* string, uint8_t start, uint8_t komma, uint8_t frac) {&lt;br /&gt;
&lt;br /&gt;
  uint8_t i;            // Zähler&lt;br /&gt;
  uint8_t flag=0;       // Merker für führende Nullen&lt;br /&gt;
&lt;br /&gt;
  // Vorkommastellen ohne führende Nullen ausgeben&lt;br /&gt;
  for(i=start; i&amp;lt;komma; i++) {&lt;br /&gt;
    if (flag==1 || string[i]!=&#039;0&#039;) {&lt;br /&gt;
      if (flag==0 &amp;amp;&amp;amp; string[0]==&#039;-&#039;) putc(&#039;-&#039;);     // negatives Vorzeichen ausgeben&lt;br /&gt;
      putc(string[i]);&lt;br /&gt;
      flag = 1;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  putc(&#039;,&#039;);                // Komma ausgeben&lt;br /&gt;
&lt;br /&gt;
  // Nachkommastellen ausgeben&lt;br /&gt;
  for(; i&amp;lt;(komma+frac); i++) putc(string[i]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktion kann auch verwendet werden, um vorzeichenlose Strings auszugeben, welche mit my_uitoa() erzeugt wurden. Da an der ersten Stelle nie ein &#039;-&#039; steht, wird auch nie ein &#039;-&#039; ausgegeben.&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:Festkommaarithmetik&amp;diff=25252</id>
		<title>Diskussion:Festkommaarithmetik</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:Festkommaarithmetik&amp;diff=25252"/>
		<updated>2007-12-28T21:42:46Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Bezug zum AD=&lt;br /&gt;
&amp;gt; Wieso wird hier der AD-Wandler gebracht?&lt;br /&gt;
&lt;br /&gt;
Weil das eine Standardanwendung ist! Es geht nach nicht um das Ansteuern das ADC sondern die Auswertung bzw. Darstellung des Ergebnisses.&lt;br /&gt;
&lt;br /&gt;
&amp;gt; Ich hätte lieber eine Differenz geteilt durch ein Differenz.&lt;br /&gt;
&lt;br /&gt;
???&lt;br /&gt;
&lt;br /&gt;
&amp;gt; AD-Wandlung ist noch ein anderes Thema zumal man dort teilweise auch durch n-1 &amp;gt;dividieren muss und nicht pauschal druch die gesamte Wortbreite.&lt;br /&gt;
&lt;br /&gt;
Das ist in der Formel enthalten. Ob man nun durch 256, 255 oder X dividiert ist egal. Es ist ein BEISPIEL und das Verfahren zu demonstrieren, keine starre Vorgabe.&lt;br /&gt;
&lt;br /&gt;
==Spiegeln==&lt;br /&gt;
Was soll denn das mit dem Spiegeln des Strings? Das kostet nur Rechenzeit und Programmspeicher. Ich bin SEHR dafür, das KOMPLETT unter den Tisch fallen zu lassen. Ausserdem ist im Abschnitt ITOA selbst gemacht mal wieder einiges doppelt hingeschrieben worden (bitte die Artikel vorher mal in RUHE lesen). Wie z.B.&lt;br /&gt;
&lt;br /&gt;
Das wars eigentlich schon.&lt;br /&gt;
&lt;br /&gt;
&amp;quot; Beachtet werden muss nur, dass bei dieser Methode die einzelnen Stellen in umgekehrter Reihenfolge entstehen: Die höchstwertigen Stellen kommen erst zum Schluss.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Das steht doch quasi schon unten drin.&lt;br /&gt;
&lt;br /&gt;
den Stringterminator). Bei dem Verfahren wird die Zahl rückwärts berechnet. Das muss bei der Ablage im Speicher berücksichtigt werden. Im Sinne der&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Und auch hier ist der Unsinn drin. RAUS DAMIT!&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Diskussion:Muluwuw&amp;diff=25251</id>
		<title>Diskussion:Muluwuw</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Diskussion:Muluwuw&amp;diff=25251"/>
		<updated>2007-12-28T21:39:42Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Was soll der Unsinn? Das ist in AVR Arithmetik schon gut drin, eine Inline-ASM Version für C ist Nonsense. Ich beantrage Löschung.&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR_Checkliste&amp;diff=25232</id>
		<title>AVR Checkliste</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR_Checkliste&amp;diff=25232"/>
		<updated>2007-12-26T22:54:58Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Sonstige Fehlerquellen bei UART/USART */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:AVR]]&lt;br /&gt;
Diese Seite soll als erste Anlaufstelle dienen, wenn der AVR-Mikrocontroller mal wieder nicht so will wie er soll. Es wird versucht, die Standardfehler aufzulisten und zu erklären, was man dagegen tun kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
=== Anderen Controller benutzt als den im Schaltplan: Pinkompatibilität sichergestellt? ===&lt;br /&gt;
&lt;br /&gt;
Nur wenige AVR-Controller sind pinkompatibel und damit untereinander austauschbar. Manchmal liegen gar die am dringensten benötigten Funktionen (ISP-Programmierung) bei anderen Controllern auf anderen Pins. Unbedingt vorher die Belegungen anhand der Datenblätter vergleichen!&lt;br /&gt;
&lt;br /&gt;
=== Fuses richtig gesetzt? ===&lt;br /&gt;
&lt;br /&gt;
Die AVR-Controller haben &#039;Fuses&#039; (deutsch: &#039;Sicherungen&#039;), die das Verhalten des Prozessors auf grundlegender Ebene bestimmen. Ein häufiger Fehler ist beispielsweise, dass die falsche Taktquelle gewählt wurde (Fuse &amp;quot;CKSEL&amp;quot; etc.): einige AVRs können mit dem internen Oszillator (&amp;lt;i&amp;gt;internal R/C&amp;lt;/i&amp;gt;), mit einem externen Oszillator (&amp;lt;i&amp;gt;external clock&amp;lt;/i&amp;gt;), mit einem Quarz (&amp;lt;i&amp;gt;external crystal&amp;lt;/i&amp;gt;) oder mit einem Resonator (&amp;lt;i&amp;gt;external R/C&amp;lt;/i&amp;gt;) betrieben werden. Wenn die Einstellung über die Fusebits nicht dazu passt (z. B. &amp;lt;i&amp;gt;external clock&amp;lt;/i&amp;gt; statt &amp;lt;i&amp;gt;external crystal&amp;lt;/i&amp;gt;), fehlt dem Controller unter Umständen der Systemtakt und er läuft nicht an, oder man bekommt auf der seriellen Schnittstelle Nonsens, oder die Timer bzw. Zeitschleifen im Programm laufen zu langsam oder zu schnell.&lt;br /&gt;
&lt;br /&gt;
Eine andere Fuse (JTAGEN) schaltet auf einem Port (z.B. PORTC auf ATMega32) die JTAG-Unterstützung ein bzw. aus. Wenn sie eingeschaltet ist, funktionieren vier Bits dieses Ports nicht wie gewohnt, da sie für die JTAG-Schnittstelle reserviert werden.&lt;br /&gt;
&lt;br /&gt;
In dem zum STK500 gehörenden Entwicklungstool &#039;AVR Studio&#039; gibt es in der Programmer-Dialogbox einen Tab &#039;Fuses&#039;, der controllerabhängig den Status der Fusebits anzeigt und Änderungen ermöglicht.&lt;br /&gt;
Siehe auch: [[AVR Fuses]].&lt;br /&gt;
&lt;br /&gt;
=== ISP-Adapter ===&lt;br /&gt;
&lt;br /&gt;
Bei ISP-Programmieradaptern für den Parallelport kann es zu Inkompatibilitäten mit manchen Ports kommen. Tritt das Problem auch auf, wenn der ISP-Adapter an einem anderen Rechner angeschlossen ist? Funktioniert es vielleicht mit einer anderen Software? Siehe auch: [[AVR In System Programmer]]).&lt;br /&gt;
&lt;br /&gt;
Bei seriellem Programmer mit Controller (STK500, Atmel AVRISP etc.): Programmieren dauert sehr lange, es gibt Fehler. Abhilfe: ISP Taktrate richtig einstellen (&amp;lt;1/4 F_CPU).&lt;br /&gt;
&lt;br /&gt;
Beim Programmieren mit dem usbasp Programmieradapter muss die Geschwindigkeit richtig eingestellt werden. Bei einem AVR der auf den eingebauten 1 Mhz läuft muss mit langsamer Geschwindigkeit (Jumper zu) programmiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Spannungsversorgung richtig angeschlossen? ===&lt;br /&gt;
&lt;br /&gt;
Der AVCC-Pin ist der Versorgungsanschluss für den AD-Wandler und den zugehörigen Port. Er ist nicht an allen AVRs vorhanden; wenn er aber vorhanden ist, so muss er auf jeden Fall angeschlossen sein, auch wenn der AD-Wandler nicht benutzt wird. Wird der AD-Wandler verwendet, sollte zur Verbesserung der Genauigkeit der AVCC-Pin über einen Lowpass-Filter angeschlossen werden (siehe Datenblatt).&lt;br /&gt;
Oft funktioniert die Programmierung des Controllers auch, wenn Vcc oder GND nicht richtig angeschlossen ist. Zur Sicherheit kann man mit einem Messgerät direkt an den Anschlüssen des AVRs kontrollieren (VCC-GND, AVCC-GND) prüfen, ob die Verbindungen korrekt sind. Es empfiehlt sich, vor dem Einsetzen bzw. Einlöten des Controllers die Versorgungsanschlüsse nochmals zu prüfen, um sicherzustellen, dass man den IC nicht durch eine zu hohe Spannung aufgrund eines Fehlers in der Versorgung zerstört.&lt;br /&gt;
&lt;br /&gt;
=== Reset-Pin korrekt beschaltet? ===&lt;br /&gt;
&lt;br /&gt;
Der Reset-Anschluss am AVR ist &#039;active-low&#039;, d. h. wenn man den Pin mit GND (Masse) verbindet, wird der Controller resettet. Zwar haben AVRs einen internen Pullup-Widerstand, der den Reset-Pin gegen VCC &amp;quot;zieht&amp;quot;, dieser ist jedoch relativ hochohmig (ca. 50 kOhm, vgl. Datenblatt) und reicht unter Umständen nicht aus, um den Reset-Pin sicher &amp;quot;hochzuhalten&amp;quot;. Als Mindestbeschaltung empfiehlt sich dringend, einen externen Pullup-Widerstand vorzusehen (typisch 10 kOhm), der den Reset-Pin mit VCC verbindet. Er sollte nicht kleiner als 4,7 kOhm sein, da der Programmieradapter sonst eventuell den Reset-Pin während des Programmiervorgangs nicht sicher auf &amp;quot;low&amp;quot; ziehen kann. Zusätzlich sollte man auch noch einen Kondensator 47 nF oder 100 nF zwischen Reset-Pin und GND anordnen. Dieses RC-Glied sorgt dafür, dass der Controller beim Einschalten der Versorgungsspannung für eine definierte Zeitspanne im Reset gehalten wird. Im laufenden Betrieb sorgt der Kondensator dafür, dass der Reseteingang unempfindlich gegenüber Spikes und Glitches wird. Er sollte deshalb unmittelbar in Pin-Nähe beim Prozessor untergebracht werden. Dieser Kondensator darf jedoch nicht verwendet werden, wenn DebugWire möglich sein soll.&lt;br /&gt;
&lt;br /&gt;
Atmel empfiehlt zusätzlich noch zum Schutz vor Überspannungen eine externe Diode nach VCC (&amp;quot;Clamp-Diode&amp;quot;), da für den Reset-Pin keine interne vorhanden ist. Diese Diode bereitet jedoch bei manchen Programmieradaptern Schwierigkeiten.&lt;br /&gt;
&lt;br /&gt;
=== Abblockkondensator(en) ordnungsgemäß installiert? ===&lt;br /&gt;
&lt;br /&gt;
Abblockkondensatoren (&amp;quot;Bunker-Kondensatoren&amp;quot;) dienen dazu, sehr kurze Versorgungsspannungseinbrüche, die durch Schaltvorgänge verursacht werden können, zu kompensieren. Diesen Zweck erfüllen sie optimal, wenn folgende Regeln eingehalten werden: &lt;br /&gt;
&lt;br /&gt;
* Ein Abblockkondensator sollte möglichst dicht am IC sitzen.&lt;br /&gt;
&lt;br /&gt;
* Jedes IC in einer Schaltung sollte einen Abblockkondensator besitzen.&lt;br /&gt;
&lt;br /&gt;
* Bei ICs mit mehreren Anschlüssen für VCC und GND sollte jedes VCC-GND-Paar mit einem eigenen Abblockkondensator beschaltet werden (z. B. AVRs in SMD-Bauform wie dem ATmega16A also mit vier Kondensatoren).&lt;br /&gt;
&lt;br /&gt;
* Es sollten keramische Kondensatoren mit einer Kapazität von 100 nF verwendet werden. Größere Kondensatoren, etwa 10 µF-Elkos, sind für diese Aufgabe &#039;&#039;nicht&#039;&#039; geeignet, weil sie &amp;quot;zu langsam&amp;quot; sind!&lt;br /&gt;
&lt;br /&gt;
=== Quarz oder Quarzoszillator angeschlossen? ===&lt;br /&gt;
&lt;br /&gt;
Die neueren AVRs haben einen internen [[Oszillator]], der im Auslieferungszustand über die entsprechenden Fuses eingeschaltet ist. In diesem Fall muss kein externer [[Quarz]] mehr angeschlossen werden. Man kann in den [[AVR Fuses|Fuse-Bits]] aber einstellen, dass man einen externen Taktgenerator (&#039;&#039;external clock&#039;&#039;, z. B. Quarzoszillator) oder externen Quarz (&#039;&#039;external crystal&#039;&#039;) verwenden möchte.&lt;br /&gt;
&lt;br /&gt;
Dann, oder wenn der AVR keinen internen Takt hat, wird an die entsprechenden Pins des Controllers ein Quarz (&#039;&#039;external crystal&#039;&#039;, Bauelement mit zwei &amp;quot;Beinchen&amp;quot;) oder Quarzoszillator (&#039;&#039;external clock&#039;&#039;, Bauelement mit vier &amp;quot;Beinchen&amp;quot;) angeschlossen. Im Falle eines Quarzes werden die XTAL-Pins mit den beiden Anschlüssen des Quarzes und jeweils mit einem Kondensator (ca. 22 bis 27 pF) gegen Masse angeschlossen. Im Falle eines Quarzoszillators reicht es aus, den Taktausgang mit dem XTAL1-Pin zu verbinden, und den XTAL2-Pin unbeschaltet zu lassen. Die Fuses sind entsprechend einzustellen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Ground-Anschlüsse beschaltet? ===&lt;br /&gt;
&lt;br /&gt;
Bei AVRs mit mehreren Ground-Anschlüssen müssen alle Anschlüsse beschaltet werden. Siehe http://www.mikrocontroller.net/forum/read-1-107259.html&lt;br /&gt;
&lt;br /&gt;
=== Alle Lötstellen in Ordnung? ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Kalte&amp;quot;, d.h. schlechte Lötstellen erkennt man an ihrer matten Oberfläche (bei bleihaltigem Lot). Bei beschädigten Lötstellen erkennt man oft einen Riss, der sich kreisförmig um die Mitte des Lötpunktes herum gebildet hat. Solche Stellen verursachen oft erst bei mechanischer Beanspruchung Probleme.&lt;br /&gt;
&lt;br /&gt;
Bei kleinen Abständen (SMD-Bauteile) müssen besonders Verbindungen zwischen benachbarten Lötungen kontrolliert werden.&lt;br /&gt;
&lt;br /&gt;
Bei Lochrasterplatinen kann man mit einem spitzen Messer (Bastelmesser) die Zwischenräume von möglichen leitenden Verbindungen befreien. &lt;br /&gt;
&lt;br /&gt;
=== Eingänge ===&lt;br /&gt;
&lt;br /&gt;
Taster müssen:&lt;br /&gt;
&lt;br /&gt;
* entprellt werden ([[Entprellung]])&lt;br /&gt;
&lt;br /&gt;
* einen [[AVR-GCC-Tutorial#Tasten_und_Schalter|Pullup-Widerstand]] besitzen, so sie &#039;&#039;active-low&#039;&#039; betrieben werden, d.h. wenn beim Tastendruck der Pin mit GND (Masse) verbunden wird (dies ist die übliche Anschaltung). Man kann einen externen Pull-Up Widerstand (typ. 10 kOhm) benutzen oder den internen aktivieren (DDR als Eingang also &amp;quot;0&amp;quot;, PORT auf &amp;quot;1&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Will man einen Taster &#039;&#039;active-high&#039;&#039; betreiben, soll also bei Tastendruck eine &amp;quot;1&amp;quot; in PIN gelesen werden, ist ein externer Pull-Down-Widerstand (typ. 10 kOhm) gegen GND anzuschließen, denn interne Pull-Downs sind nicht verfügbar.&lt;br /&gt;
&lt;br /&gt;
Symptome: Aufgrund des Prellens bekommt man bei einem Tastendruck statt eines Signals mehrere, und beim fehlenden Pullup fängt man sich Störungen (z.B. das 50 Hz-Netzbrummen) ein, da der Pin nicht auf einem &amp;quot;definierten Pegel&amp;quot; liegt, wenn der Taster nicht geschlossen ist. Soll z.B. bei einem Tastendruck eine LED angehen, dann leuchtet die LED durch das Netzbrummen plötzlich mit 50 Hz anstatt aus zu sein.&lt;br /&gt;
&lt;br /&gt;
* active low: ein Anschluss des Tasters an den Port-Pin, den anderen Taster-Anschluss an GND; internen Pull-Up-Widerstand aktivieren oder externen Widerstand zwischen Port-Pin und VCC.&lt;br /&gt;
&lt;br /&gt;
* active high: Taster zwischen Port-Pin und VCC; externen Widerstand zwischen Port-Pin und GND.&lt;br /&gt;
&lt;br /&gt;
=== Ausgänge ===&lt;br /&gt;
&lt;br /&gt;
Man sollte darauf achten, das &amp;quot;kritische&amp;quot; Ausgänge, d.h. Ausgänge, über die nicht &amp;quot;nur&amp;quot; eine LED geschaltet wird, einen definierten Zustand haben, wenn der Portpin auf &amp;quot;Eingang&amp;quot; und damit hochohmig geschaltet ist. Dadurch wird sichergestellt, dass beim Einschalten nicht kurz ein Verbraucher geschaltet wird (z.B. &amp;quot;Zucken&amp;quot; eines Motors). Dies kann man bewerkstelligen, indem man extern Widerstände (auch hier Pull-Up bzw. Pull-Down genannt, typ. 10 kOhm) an den Ausgangs-Pins vorsieht, die den Ausgang auf den gewünschten Zustand ziehen.&lt;br /&gt;
&lt;br /&gt;
=== Besonderheiten bei ATmega64 / ATmega128 ===&lt;br /&gt;
&lt;br /&gt;
Der ATmega64 und der ATmega128 haben zwei besondere Fallstricke, über die man bei nicht ausreichendem Datenblattstudium leicht stolpert. &lt;br /&gt;
&lt;br /&gt;
* Der erste betrifft den Anschluss der ISP-Signale (MISO, MOSI, SCK, RESET). Dieser erfolgt nicht wie bei den meisten anderen AVR-Controllern an den gleichnamigen Pins, sondern an PDI, PDO, SCK und RESET. Die Pinzuordnung ist:  MOSI-&amp;gt;PE0 (Pin 2), MISO-&amp;gt;PE1 (Pin 3), SCK-&amp;gt;PB1 (Pin 11) und RESET-&amp;gt;RESET (Pin 20). PEN (Pin 1) hat für normale ISP-Adapter keine Bedeutung und kann offen gelassen oder direkt mit Vcc verbunden werden. Die Benutzung der Pins PDI und PDO anstelle von MOSI und MISO gilt zusätzlich für einige ATMega128-Derivate im 64-Pin-TQFP-Gehäuse (darunter AT90CAN128, ATMega1281/2561)! Im Zweifelsfall im Datenblatt (Pin Configuration) nachsehen, ob PE0 und PE1 mit &amp;quot;PDI&amp;quot; bzw. &amp;quot;PDO&amp;quot; beschriftet sind.&lt;br /&gt;
&lt;br /&gt;
* Die zweite kleine &amp;quot;Gemeinheit&amp;quot; betrifft die &#039;&#039;&#039;M103C-Fuse&#039;&#039;&#039; (Atmega103 Compatibility Mode). Diese ist bei fabrikneuen Atmega64 und Atmega128 gesetzt und sorgt dafür, dass sich diese wie ein Atmega103 verhalten.&lt;br /&gt;
** Andererseits hat dies zur Folge, dass ein für den Atmega64 oder Atmega128 geschriebenes Programm beim ersten &amp;quot;ret&amp;quot; abstürzt, da der SRAM an einer anderen Stelle liegt als erwartet und somit der Prozessor keine gültige Rücksprungadresse vom Stack holen kann. &lt;br /&gt;
** Außerdem funktionieren einige IO-Pins an PORTC, PORTF und PORTG anders.&lt;br /&gt;
** Für weitere Infos bzgl. [[TWI]], [[UART]], [[Timer]], [[Bootloader]] und Kalibrierung des internen RC-Oszillators unbedingt das Datenblatt lesen.&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== Alle Interruptvektoren definiert? ===&lt;br /&gt;
&lt;br /&gt;
Wenn man irgendwelche [[Interrupt]]vektoren verwendet, sollte man alle definieren, auch die nicht benutzten. Passiert es dann aufgrund eines Fehlers, dass ein Interrupt unbeabsichtigt ausgeführt wird, so führt der Controller dann eine definierte Aktion aus. Benutzt man eine Hochsprache wie C oder Basic, nimmt einem der Compiler diese Arbeit ab.&lt;br /&gt;
&lt;br /&gt;
Ausserdem sollte man immer im Datenblatt des Controllers nachsehen, wie die Interrupttabelle aufgebaut ist. Die kleinen AVRs verwenden ein &#039;&#039;&#039;RCALL&#039;&#039;&#039; zum Anspringen der Interrupts, welches 1 Wort lang ist. Die grossen AVRs mit mehr als 8 KiBi FLASH verwenden ein &#039;&#039;&#039;CALL&#039;&#039;&#039;, welches 2 Worte lang ist. Verwendet man nun  den falschen Befehl, verschieben sich die Einsprungadressen und das Chaos ist perfekt.&lt;br /&gt;
&lt;br /&gt;
In einem komplett interruptlosen Programm kann man auf die Interrupttabelle selbstverständlich verzichten (erkennbar daran, dass nirgendwo im Code Interrupts mittels Assembler-Befehl &#039;&#039;&#039;sei&#039;&#039;&#039; eingeschaltet werden).&lt;br /&gt;
&lt;br /&gt;
=== Alle Konfigurationsregister korrekt initialisiert? ===&lt;br /&gt;
&lt;br /&gt;
Alle benötigten Konfigurationsregister (auch &amp;quot;I/O-Register&amp;quot; genannt) eines Mikrocontrollers müssen korrekt initialisiert werden. Bei Fehlfunktionen sollten diese Konfigurationen noch einmal mit dem Datenblatt abgeglichen werden. &lt;br /&gt;
Manchmal ist es auch sinnvoll, bestimmte Funktionen explizit abzuwählen. Ein Beispiel hierfür ist der [[Analog-Komparator]] des AVR: Schaltet man diesen ab, wenn man ihn nicht benötigt, kann man dadurch ein wenig Strom sparen. &lt;br /&gt;
Wenn man besonders &amp;quot;sauber&amp;quot; programmieren möchte, initialisiert man Konfigurationsregister immer, d.h. auch wenn sie nicht im Programm verwendet werden. Dies verhindert mögliche zufällige Fehlkonfigurationen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Stackpointer initialisiert? (Nur in Assembler relevant) ===&lt;br /&gt;
&lt;br /&gt;
Fehlerbeschreibung: &#039;&#039;Das Programm lief, bis ein &amp;quot;rcall&amp;quot; oder ein Interrupt eingefügt wurde. Danach ging plötzlich gar nichts mehr.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wahrscheinliche Ursache: Der Stack ist ein spezieller Bereich im RAM, der von Sprungbefehlen und Interruptaufrufen dazu verwendet wird, die Rücksprungadresse ins Hauptprogramm zu speichern.  Da der Stack bei den AVRs nach &amp;quot;unten&amp;quot; wächst, d.h. in Richtung Anfang des RAMs, wird er üblicherweise ans Ende des RAMs gelegt. Der Stack muss vor der ersten Benutzung, am besten direkt am Anfang des Programms, initialisiert werden (sonst ist nach einem Sprung in ein Unterprogramm die Rücksprungadresse undefiniert und das Programm wird an einer unvorhersehbaren Stelle weiter ausgeführt, was in der Regel einen Absturz zur Folge hat). &amp;quot;Den Stack initialisieren&amp;quot; bedeutet, den Stackpointer auf den gewünschten &amp;quot;Startwert&amp;quot; zu setzen. Meistens wird dafür die letzte RAM-Adresse gewählt. &lt;br /&gt;
&lt;br /&gt;
Die Initialisierung unterscheidet sich geringfügig je nach verwendetem AVR. Bei den alten AT90xxxx und den ATtinys ist sie mit zwei Zeilen erledigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
       ldi r16, RAMEND   ;Die Adresse der letzten Stelle im RAM in r16 laden&lt;br /&gt;
       out SPL, r16      ;Die Adresse in das Register SPL ausgeben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Da die ATmegas mehr RAM haben, reicht das 8 bit-Register SPL nicht mehr, und es ist ein zweites (SPH) dazugekommen.  Die Initialisierung des Stacks erfordert hier vier Zeilen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
       ldi r16, LOW(RAMEND)    ;Untere 8 bit des 16 bit-Wertes RAMEND laden&lt;br /&gt;
       out SPL, r16&lt;br /&gt;
       ldi r16, HIGH(RAMEND)   ;Obere 8 bit laden&lt;br /&gt;
       out SPH, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 16bit-Register in richtiger Reihenfolge geladen/gelesen? (Nur in Assembler relevant) ===&lt;br /&gt;
&lt;br /&gt;
Bei den 16-bit Registern (z.B. Timer1) genau die Hinweise im Kapitel &amp;quot;Accessing 16-bit Registers&amp;quot; beachten!&lt;br /&gt;
&lt;br /&gt;
So muss z.B. das High-Byte VOR dem Low-Byte geschrieben werden, weil mit dem Schreiben des Low Byte das gesamte Register &amp;quot;übernommen&amp;quot; wird.&lt;br /&gt;
Beim Lesen muss zuerst das Low-Byte gelesen werden, dann das High-Byte.&lt;br /&gt;
Es wird dadurch garantiert, dass sich zwischen den beiden einzelnen Befehlen nicht noch das Register weiter verändert, z.B. beim 16bit Counter.&lt;br /&gt;
Möglich wird das durch ein &amp;quot;temporary Register&amp;quot;, von dem es aber nur eines gibt - was beachtet werden muss, da zwischen diesen beiden einzelnen Lese-/Schreib-Befehlen kein Interrupt auftreten darf, der selber das temporary Rgegister benutzt.&lt;br /&gt;
&lt;br /&gt;
In C oder Basic kümmert sich der Compiler um das Problem - man liest oder beschreibt dann einfach z.B. TCNT1 anstatt TCNT1H und TCNT1L einzeln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Flag richtig gelöscht? ===&lt;br /&gt;
&lt;br /&gt;
Ein beliebter Fehler ist, dass man überliest, dass gesetzte Flags (z.B. die Interrupt-Flags) durch beschreiben mit einer &#039;1&#039; und nicht mit einer &#039;0&#039; gelöscht werden! &lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* das Flag ist zuerst &#039;0&#039;&lt;br /&gt;
* das Ereignis (z.B. Zählerüberlauf, also Bit TOV0 in TIFR) tritt auf und setzt das jeweilige Bit im Register auf &#039;1&#039;&lt;br /&gt;
* wir haben auf die &#039;1&#039; gewartet und wollen das Bit nun wieder löschen und müssen dafür eine &#039;1&#039; (eins!) in das Register schreiben, &#039;&#039;&#039;keine&#039;&#039;&#039; &#039;0&#039;. Trotzdem wird das Bit dadurch &#039;0&#039; z.B.:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    TIFR = (1&amp;lt;&amp;lt;TOV0);&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ganze ist kein Fehler, sondern hat seine guten Gründe.&lt;br /&gt;
&lt;br /&gt;
Wenn Interrupts zugelassen sind (z.B. in TIMSK) und die Interruptroutine aufgerufen wird, werden diese Flags zumeist automatisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
Der UART-Empfangs-Interrupt (RXC in UCSRA) jedoch nicht! Der wird gelöscht durch Auslesen des Empfangsregisters (UDR). Hier liegt wiederum ein beliebter Stolperstein: Man prüft oft zuerst in der Interruptroutine die Fehlerflags (z.B. Parity) und macht dann manchmal den Fehler das empfangene Zeichen im Fehlerfall nicht auszulesen. Das führt dann zum Absturz (der IRQ wird immer wieder aufgerufen) - man muss also immer das UDR lesen auch wenn etwas Fehlerhaftes drinsteht.&lt;br /&gt;
&lt;br /&gt;
Zusätzlich ist zu beachten, dass in C die Interrupt-Flags &#039;&#039;&#039;nicht&#039;&#039;&#039; mit einer Veroderung des Registers (z.B. TIFR |= (1 &amp;lt;&amp;lt; TOV0)) gelöscht werden sollten, da bei dieser Operation alle anderen evtl. gesetzten Flags im betreffenden Register ebenfalls gelöscht werden. Es ist die Schreibweise von oben zu benutzen (also TIFR = (1 &amp;lt;&amp;lt; TOV0))!&lt;br /&gt;
&lt;br /&gt;
== Serielle Verbindungen ==&lt;br /&gt;
&lt;br /&gt;
=== I²C/TWI ===&lt;br /&gt;
&lt;br /&gt;
Sind die Leitungen SCL und SDA mit einem Pullup-Widerstand ausgestattet? Die I²C-Bus-Leitungen SCL und SDA müssen über einen Pullup-Widerstand mit einem Wert von 4,7 kOhm bis 10 kOhm mit der Versorgungsspannung (+5 V) verbunden sein.&lt;br /&gt;
&lt;br /&gt;
=== UART/USART ===&lt;br /&gt;
&lt;br /&gt;
==== Übertragungsprobleme durch falschen oder ungenauen Takt ====&lt;br /&gt;
&lt;br /&gt;
* Der interne [[Oszillator]] ist recht ungenau und nicht temperaturstabil. Daher kann die Umgebungstemperatur auch den [[UART | USART]]-Takt verändern. Für serielle, asynchrone Kommunikation per UART sollte man deshalb immer einen Quarz(oszillator) verwenden, eagl bei welcher Baudrate (3% sind immer 3%, egal ob bei 1200 oder 9600 [[Baud]]). Falls doch der interne Oszillator verwendet wird, wurde er für die richtige Frequenz kalibriert?&lt;br /&gt;
&lt;br /&gt;
* Nicht mit allen Quarzen kann man alle Baudraten genau genug erzeugen; deswegen gibt es [[Baudratenquarz]]e wie z.B. 3,6864 MHz. Näheres steht im Datenblatt.&lt;br /&gt;
&lt;br /&gt;
* Geschieht die Konfiguration des U(S)ART automatisch durch die Programmiersprache (z.B. in [[Bascom AVR | BASCOM]]), dann muss dort der Takt &#039;&#039;&#039;genau&#039;&#039;&#039; angegeben werden. Setzt man z.B. einen 3,6864 MHz-Quarz ein, darf man dort nicht einfach &amp;quot;4 MHz&amp;quot; angeben, weil dann die Abweichung mit ca. 8% viel zu hoch wäre. Damit funktioniert die Übertragung nicht mehr.&lt;br /&gt;
&lt;br /&gt;
* Wenn ein externer Oszillator oder Quarz angeschlossen ist: Sind die [[AVR Fuses | Fuses]] des Controllers so gesetzt, dass er auch verwendet wird?&lt;br /&gt;
&lt;br /&gt;
* Um zu prüfen, ob der externe Quarz auch wirklich verwendet wird, kann man mittels _delay_ms() einfach mal eine LED im Sekundentakt blinken lassen. Den Unterschied zwischen Quarz und internem RC-Oszillator kann man bei grösseren Frequenzunterschieden leicht sehen. Allerdings muss man auch hier aufpassen. Siehe [[AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29 |AVR-Tutorial]].&lt;br /&gt;
&lt;br /&gt;
* Erscheinen im Terminalprogramm kryptische Zeichen anstatt ordentlichen Buchstaben (z.B. ü statt A), liegt das zu 99,9% an einer falsch eingestellten/erzeugten Baudrate im Mikrocontroller.&lt;br /&gt;
&lt;br /&gt;
* In den Tutorials für [[AVR-Tutorial: UART |Assembler]] und [http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Der_UART GCC] wird die Nutzung recht gut erklärt und mit Beispielen hinterlegt.&lt;br /&gt;
&lt;br /&gt;
==== Sonstige Fehlerquellen bei UART/USART ====&lt;br /&gt;
&lt;br /&gt;
* Funktioniert die Datenübertragung zum PC nur solange der Programmieradapter eingesteckt ist, deutet dies auf ein Problem mit der Masse hin (z.B. GND-Anschluss am RS232-Stecker nicht belegt oder dergleichen).&lt;br /&gt;
&lt;br /&gt;
* Der Pegelwandler/Inverter (z.B. MAX232) muss mit Kondensatoren für die internen Ladungspumpen beschaltet werden. Beim MAX232 sind dies mindestens vier Kondensatoren &amp;gt;= 1 µF (Polung beachten!), beim MAX202 und MAX3232 vier Kondensatoren &amp;gt;= 100 nF. Hinzu kommt der obligatorische Abblockkondensator (100 nF keramisch) zwischen VCC und GND des ICs. Das jeweilige Datenblatt gibt Auskunft über die Beschaltung bei &amp;quot;typischer Anwendung&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* TX/RX vertauscht? Modem- oder Nullmodemkabel?&lt;br /&gt;
&lt;br /&gt;
* Die Hardware und Verkabelung kann man schnell prüfen, indem man das Pin R1OUT bzw. R2OUT mit T1IN bzw. T2IN verbindet (je nachdem welches verwendet wird). Dann sollte man auf dem PC ein Terminalprogramm starten (z.B. Hyperterminal) und Zeichen eigeben können und sofort angezeigt bekommen, egal mit welcher Baudrate. Lediglich die Flusssteuerung muss auf &amp;quot;Kein&amp;quot; eingestellt werden. Man muss aber beachten, dass T1IN bzw. T2IN für diesen Test &#039;&#039;&#039;nicht&#039;&#039;&#039; mit dem Mikrocontroller verbunden sein darf, weil sonst zwei Ausgänge miteinander verbunden sind, was nicht wirklich gut ist (siehe [[Ausgangsstufen Logik-ICs]]). Wenn der uC gesockelt ist kann man ihn dafür einfach aus dem Sockel nehmen und die RX/TX Pins mit einem Stück Draht verbinden.&lt;br /&gt;
&lt;br /&gt;
== Analog-Digital-Wandler ==&lt;br /&gt;
&lt;br /&gt;
* Sind die Pullup-Widerstände deaktiviert?&lt;br /&gt;
&lt;br /&gt;
== Web-Links ==&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc2521.pdf Application Note AVR042: AVR Hardware Design Considerations]&lt;br /&gt;
* [http://www.atmel.com/dyn/resources/prod_documents/doc1619.pdf Application Note AVR040: EMC Design Considerations]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Uhr&amp;diff=25228</id>
		<title>AVR-Tutorial: Uhr</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Uhr&amp;diff=25228"/>
		<updated>2007-12-26T14:44:18Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Texte und Beispiele überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Eine beliebte Übung für jeden Programmierer ist die Implementierung einer Uhr. Die meisten Uhren bestehen aus einem Taktgeber und einer Auswerte- und Anzeigevorrichtung. Wir wollen hier beides mittels eines Programmes in einem Mikrocontroller realisieren. Voraussetzung für diese Fallstudie ist das Verständnis der Kapitel über&lt;br /&gt;
&lt;br /&gt;
* [[AVR-Tutorial: LCD| Ansteuerung eines LC-Displays]]&lt;br /&gt;
* [[AVR-Tutorial: Timer| Timer]]&lt;br /&gt;
&lt;br /&gt;
== Aufbau und Funktion ==&lt;br /&gt;
&lt;br /&gt;
Die Aufgabe des Taktgebers, der uns einen möglichst konstanten und genauen Takt liefert, übernimmt ein Timer. Der Timer ermöglicht einen einfachen Zugang zum Takt, die der AVR vom Quarz abgreift. Wie schon im Einführungskapitel über den [[AVR-Tutorial:_Timer|Timer]] wird dazu einen Timer Overflow Interrupt installiert und in diesem Interrupt wird die eigentliche Uhr hochgezählt. Die Uhr selbst besteht aus 4 Registern. 3 davon repräsentieren die Sekunden, Minuten und Stunden unserer Uhr. Nach jeweils einer Sekunde wird das Sekundenregister um 1 erhöht. Sind 60 Sekunden vergangen, dann wird das Sekundenregister wieder auf 0 gesetzt und dafür das Minutenregister um 1 erhöht (Überlauf). Nach 60 Minuten werden die Minuten wieder auf 0 gesetzt und für diese 60 Minuten eine Stunde mehr gezählt. Nach 24 Stunden schliesslich werden die Stunden wieder auf 0 gesetzt, ein ganzer Tag ist vergangen.&lt;br /&gt;
&lt;br /&gt;
Aber wozu das 4. Register?&lt;br /&gt;
&lt;br /&gt;
Der Mikrocontroller wird mit 4MHz betrieben. Bei einem Teiler von 1024 zählt der Timer also mit 4000000 / 1024 = 3906.25 Pulsen pro Sekunde. Der Timer muss einmal bis 256 zählen, bis er einen Overflow auslöst. Das heist, es ereignen sich 3906.25 / 256 = 15.2587 Overflows pro Sekunde. Die Aufgabe des 4. Registers ist es nun diese 15 Overflows zu zählen. Bei Auftreten des 15.ten Overflow ist 1 Sekunde vergangen. Dass dies nicht exakt stimmt, da ja die Division auch Nachkommastellen aufwies, wird im Moment der Einfachheit halber ignoriert. In einem späteren Abschnitt wird darauf noch eingegangen.&lt;br /&gt;
&lt;br /&gt;
Im Overflow Interrupt wird also diese Kette von Zählvorgängen auf den Sekunden, Minuten und Stunden durchgeführt und anschliessend zur Anzeige gebracht. Dazu werden die in einem vorhergehenden Kapitel entwickelten [[AVR-Tutorial:_LCD|LCD Funktionen]] benutzt.&lt;br /&gt;
&lt;br /&gt;
== Das erste Programm ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
.def flag  = r19&lt;br /&gt;
 &lt;br /&gt;
.def SubCount = r21&lt;br /&gt;
.def Sekunden = r22&lt;br /&gt;
.def Minuten  = r23&lt;br /&gt;
.def Stunden  = r24&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
        rjmp    main                ; Reset Handler&lt;br /&gt;
.org OVF0addr&lt;br /&gt;
        rjmp    timer0_overflow     ; Timer Overflow Handler&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp1&lt;br /&gt;
        ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp1&lt;br /&gt;
    &lt;br /&gt;
        rcall   lcd_init&lt;br /&gt;
        rcall   lcd_clear&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
        ldi     temp1, 0b00000101   ; Teiler 1024&lt;br /&gt;
        out     TCCR0, temp1&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp1, 0b00000001   ; TOIE0: Interrupt bei Timer Overflow&lt;br /&gt;
        out     TIMSK, temp1&lt;br /&gt;
 &lt;br /&gt;
        clr     Minuten             ; Die Uhr auf 0 setzen&lt;br /&gt;
        clr     Sekunden&lt;br /&gt;
        clr     Stunden&lt;br /&gt;
        clr     SubCount&lt;br /&gt;
        clr     Flag                ; Merker löschen&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
 &lt;br /&gt;
loop:&lt;br /&gt;
        cpi     flag,0&lt;br /&gt;
        breq    loop                ; Flag im Interrupt gesetzt?&lt;br /&gt;
        ldi     flag,0              ; flag löschen&lt;br /&gt;
  &lt;br /&gt;
        rcall   lcd_clear           ; das LCD löschen&lt;br /&gt;
        mov     temp1, Stunden      ; und die Stunden ausgeben&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
        ldi     temp1, &#039;:&#039;          ; zwischen Stunden und Minuten einen &#039;:&#039;&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
        mov     temp1, Minuten      ; dann die Minuten ausgeben&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
        ldi     temp1, &#039;:&#039;          ; und noch ein &#039;:&#039;&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
        mov     temp1, Sekunden     ; und die Sekunden&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
&lt;br /&gt;
        rjmp    loop&lt;br /&gt;
 &lt;br /&gt;
timer0_overflow:                    ; Timer 0 Overflow Handler&lt;br /&gt;
&lt;br /&gt;
        push    temp1               ; temp 1 sichern&lt;br /&gt;
        in      temp1,sreg          ; SREG sichern&lt;br /&gt;
&lt;br /&gt;
        inc     SubCount            ; Wenn dies nicht der 15. Interrupt&lt;br /&gt;
        cpi     SubCount, 15        ; ist, dann passiert gar nichts&lt;br /&gt;
        brne    end_isr&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     SubCount            ; SubCount rücksetzen&lt;br /&gt;
        inc     Sekunden            ; plus 1 Sekunde&lt;br /&gt;
        cpi     Sekunden, 60        ; sind 60 Sekunden vergangen?&lt;br /&gt;
        brne    Ausgabe             ; wenn nicht kann die Ausgabe schon&lt;br /&gt;
                                    ; gemacht werden&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Sekunden            ; Sekunden wieder auf 0 und dafür&lt;br /&gt;
        inc     Minuten             ; plus 1 Minute&lt;br /&gt;
        cpi     Minuten, 60         ; sind 60 Minuten vergangen ?&lt;br /&gt;
        brne    Ausgabe             ; wenn nicht, -&amp;gt; Ausgabe&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Minuten             ; Minuten zurücksetzen und dafür&lt;br /&gt;
        inc     Stunden             ; plus 1 Stunde&lt;br /&gt;
        cpi     Stunden, 24         ; nach 24 Stunden, die Stundenanzeige&lt;br /&gt;
        brne    Ausgabe             ; wieder zurücksetzen&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Stunden             ; Stunden rücksetzen&lt;br /&gt;
&lt;br /&gt;
Ausgabe:&lt;br /&gt;
        ldi     flag,1              ; Flag setzen, LCD updaten&lt;br /&gt;
&lt;br /&gt;
end_isr:&lt;br /&gt;
&lt;br /&gt;
        out     sreg,temp1          ; sreg wieder herstellen&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        reti                        ; das wars. Interrupt ist fertig&lt;br /&gt;
&lt;br /&gt;
; Eine Zahl aus dem Register temp1 ausgeben&lt;br /&gt;
&lt;br /&gt;
lcd_number:&lt;br /&gt;
        push    temp2               ; register sichern,&lt;br /&gt;
                                    ; wird für Zwsichenergebnisse gebraucht     &lt;br /&gt;
        ldi     temp2, &#039;0&#039;         &lt;br /&gt;
lcd_number_10:                &lt;br /&gt;
        subi    temp1, 10           ; abzählen wieviele Zehner in&lt;br /&gt;
        brcs    lcd_number_1        ; der Zahl enthalten sind&lt;br /&gt;
        inc     temp2&lt;br /&gt;
        rjmp    lcd_number_10&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
        rcall   lcd_data            ; die Zehnerstelle ausgeben&lt;br /&gt;
        subi    temp1, -10          ; 10 wieder dazuzählen, da die&lt;br /&gt;
                                    ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                                    ; abgezogen hat&lt;br /&gt;
                                    ; das Subtrahieren von -10&lt;br /&gt;
                                    ; = Addition von +10 ist ein Trick&lt;br /&gt;
                                    ; da kein addi Befehl existiert&lt;br /&gt;
        ldi     temp2, &#039;0&#039;          ; die übrig gebliebenen Einer&lt;br /&gt;
        add     temp1, temp2        ; noch ausgeben&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
&lt;br /&gt;
        pop     temp2               ; Register wieder herstellen&lt;br /&gt;
        ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der ISR wird nur die Zeit in den Registern neu berechnet, die Ausgabe auf das LCD erfolgt in der Hauptschleife. Das ist notwendig, da die LCD-Ausgabe bisweilen sehr lange dauern kann. Wenn sie länger als ~2/15 Sekunden dauert werden Timerinterrupts &amp;quot;verschluckt&amp;quot; und unsere Uhr geht noch mehr falsch. Dadurch, dass aber die Ausgabe in der Hauptschleife durchgeführt wird, welche jederzeit durch einen Timerinterrupt unterbrochen werden kann, werden keine Timerinterrupts verschluckt. Das ist vor allem wichtig, wenn mit höheren Interruptfrequenzen geabreitet wird, z.B. 1/100s im Beispiel weiter unten. Auch wenn in diesem einfachen Beispiel die Ausgabe bei weitem nicht 2/15 Sekunden dauert, sollte man sich diesen Programmierstil allgemein angewöhnen. Siehe auch [[Interrupt]].&lt;br /&gt;
&lt;br /&gt;
Im Moment gibt es keine Möglichkeit, die Uhr auf eine bestimmte Uhrzeit einzustellen. Um dies tun zu können müssten noch zusätzlich Taster an den Mikrocontroller angeschlossen werden, mit deren Hilfe die Sekunden, Minuten und Stunden händisch vergrößert bzw. verkleinert werden können. Studieren Sie mal die Bedienungsanleitung einer käuflichen Digitaluhr und versuchen sie zu beschreiben, wie dieser Stellvorgang bei dieser Uhr vor sich geht. Sicherlich werden Sie daraus eine Idee entwickeln können, wie ein derartiges Stellen mit der hier vorgestellten Digitaluhr funktionieren könnte. Als Zwischenlösung kann man im Programm die Uhr beim Start anstelle von 00:00:00 z.B. auch auf 20:00:00 stellen und exakt mit dem Start der Tagesschau starten.&lt;br /&gt;
&lt;br /&gt;
== Ganggenauigkeit ==&lt;br /&gt;
&lt;br /&gt;
Wird die Uhr mit einer gekauften Uhr verglichen, so stellt man schnell fest, dass die ganz schön ungenau geht. Sie geht vor! Woran liegt das? Die Berechnung sieht so aus:&lt;br /&gt;
* Frequenz des Quarzes: 4.0 MHz&lt;br /&gt;
* Vorteiler des Timers: 1024&lt;br /&gt;
* Überlauf alle 256 Timertakte&lt;br /&gt;
&lt;br /&gt;
Daraus errechnet sich, daß in einer Sekunde 4000000 / 1024 / 256 = 15.258789 Overflow Interrupts auftreten. Im Programm wird aber bereits nach 15 Overflows eine Sekunde weitergeschaltet, daher geht die Uhr vor. Rechnen wir etwas:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F_r = (\frac {15}{15,258789}-1) \cdot 100% = -1,69%&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So wie bisher läuft die Uhr also rund 1.7 % zu schnell. In einer Minute ist das immerhin etwas mehr als eine Sekunde. Im Grunde ist das ein ähnliches Problem wie mit unserer Jahreslänge. Ein Jahr dauert nicht exakt 365 Tage, sondern in etwa einen viertel Tag länger. Die Lösung, die im Kalender dafür gemacht wurde - der Schalttag -, könnte man fast direkt übernehmen. Nach 3 Stück 15er Overflow Sekunden folgt eine Sekunde für die 16 Overflows ablaufen müssen. Wie sieht die Rechnung bei einem 15, 15, 15, 16 Schema aus? Für 4 Sekunden werden exakt 15.258789 * 4 = 61,035156 Overflow Interrupts benötigt. Mit einem 15, 15, 15, 16 Schema werden in 4 Sekunden genau 61 Overflow Interrupts durchgeführt. Der relative Fehler beträgt dann&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F_r = (\frac {61}{61,035156}-1) \cdot 100% = -0,0575%&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit diesem Schema ist der Fehler beträchtlich gesunken. Nur noch 0.06%. Bei dieser Rate muss die Uhr immerhin etwas länger als 0,5 Stunden laufen, bis der Fehler auf eine Sekunde angewachsen ist. Das sind aber immer noch 48 Sekunden pro Tag bzw. 1488 Sekunden (=24,8 Minuten) pro Monat. So schlecht sind nicht mal billige mechanische Uhren!&lt;br /&gt;
&lt;br /&gt;
Jetzt könnte man natürlich noch weiter gehen und immer kompliziertere &amp;quot;Schalt-Overflow&amp;quot;-Schemata austüfteln und damit die Genauigkeit näher an 100% bringen. Aber gibt es noch andere Möglichkeiten?&lt;br /&gt;
&lt;br /&gt;
Im ersten Ansatz wurde ein Vorteiler von 1024 eingesetzt. Was passiert bei einem anderen Vorteiler? Nehmen wir mal einen Vorteiler von 64. Das heist, es müssen ( 4000000 / 64 ) / 256 = 244.140625 Overflows auflaufen bis 1 Sekunde vergangen ist. Wenn also 244 Overflows gezählt werden, dann beläuft sich der Fehler auf&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;F_r = (\frac {244}{244,140625}-1) \cdot 100% = -0,0576%&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nicht schlecht. Nur durch Verändern von 2 Zahlenwerten im Programm (Teilerfaktor und Anzahl der Overflow Interrupts bis zu einer Sekunden) kann die Genauigkeit gegenüber dem ursprünglichen Overflow-Schema beträchtlich gesteigert werden. Aber geht das noch besser? Ja das geht. Allerdings nicht mit dem Overflow Interrupt.&lt;br /&gt;
&lt;br /&gt;
== Der CTC Modus des Timers ==&lt;br /&gt;
&lt;br /&gt;
Worin liegt den das eigentliche Problem, mit dem die Uhr zu kämpfen hat? Das Problem liegt darin, dass jedesmal ein kompletter Timerzyklus bis zum Overflow abgewartet werden muss, um darauf zu reagieren. Da aber nur jeweils ganzzahlige Overflowzyklen abgezählt werden können, heisst das auch, dass im ersten Fall nur in Vielfachen von 1024 * 256 = 262144 Takten operiert werden kann, während im letzten Fall immerhin schon eine Granulierung von 64 * 256 = 16384 Takten erreicht wird. Aber offensichtlich ist das nicht genau genug. Bei 4 MHz entsprechen 262144 Takte bereits einem Zeitraum von 65,5ms, während 16384 Takte einem Zeitbedarf von 4,096ms entsprechen. Beide Zahlen teilen aber 1000ms nicht ganzzahlig. Und daraus entsteht der Fehler. Angestrebt wird ein Timer der seinen &amp;lt;i&amp;gt;Overflow&amp;lt;/i&amp;gt; so erreicht, dass sich ein ganzzahliger Teiler von 1 Sekunde einstellt. Dazu müssten man dem Timer aber vorschreiben können, bei welchem Zählerstand der &amp;lt;i&amp;gt;Overflow&amp;lt;/i&amp;gt; erfolgen soll. Und genau dies ist im &#039;&#039;&#039;CTC&#039;&#039;&#039; Modus, allerdings nur beim Timer 1, möglich. &#039;&#039;&#039;CTC&#039;&#039;&#039; bedeutet &amp;quot;&#039;&#039;&#039;C&#039;&#039;&#039;lear &#039;&#039;&#039;T&#039;&#039;&#039;imer on &#039;&#039;&#039;C&#039;&#039;&#039;ompare match&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Timer 1, ein 16 Bit Timer, wird mit einem Vorteiler von 1 betrieben. Dadurch wird erreicht, dass der Timer mit höchster Zeitauflösung arbeiten kann. Bei jedem Ticken des Systemtaktes von 4 MHz wird auch der Timer um 1 erhöht. Zusätzlich wird noch das WGM12 Bit bei der Konfiguration gesetzt. Dadurch wird der Timer in den &#039;&#039;&#039;CTC&#039;&#039;&#039; Modus gesetzt. Dabei wird der Inhalt des Timers hardwaremäßig mit dem Inhalt des &#039;&#039;&#039;OCR1A&#039;&#039;&#039; Registers verglichen. Stimmen beide überein, so wird der Timer auf 0 zurückgesetzt und im nächsten Taktzyklus ein &#039;&#039;&#039;OCIE1A&#039;&#039;&#039; Interrupt ausgelöst. Dadurch ist es möglich exakt die Anzahl an Taktzyklen festzulegen, die von einem Interrupt zum nächsten vergehen sollen. Das Compare Register &#039;&#039;&#039;OCR1A&#039;&#039;&#039; wird mit dem Wert 39999 vorbelegt. Dadurch vergehen exakt 40000 Taktzyklen von einem Compare Interrupt zum nächsten. &amp;quot;Zufällig&amp;quot; ist dieser Wert so gewählt, daß bei einem Systemtakt von 4 MHz von einem Interrupt zum nächsten genau 1 hunderstel Sekunde vergeht. Bei einem möglichen Umbau der Uhr zu einer Stoppuhr könnte sich das als nützlich erweisen. Im Interrupt wird das Hilfsregister SubCount bis 100 hochgezählt und nach 100 Interrupts kommt wieder die Sekundenweiterschaltung wie oben in Gang.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp1 = r16&lt;br /&gt;
.def temp2 = r17&lt;br /&gt;
.def temp3 = r18&lt;br /&gt;
.def Flag  = r19&lt;br /&gt;
 &lt;br /&gt;
.def SubCount = r21&lt;br /&gt;
.def Sekunden = r22&lt;br /&gt;
.def Minuten  = r23&lt;br /&gt;
.def Stunden  = r24&lt;br /&gt;
&lt;br /&gt;
.org 0x0000&lt;br /&gt;
           rjmp    main             ; Reset Handler&lt;br /&gt;
.org OC1Aaddr&lt;br /&gt;
           rjmp    timer1_compare   ; Timer Compare Handler&lt;br /&gt;
&lt;br /&gt;
.include &amp;quot;lcd-routines.asm&amp;quot;&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp1, LOW(RAMEND)  ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp1&lt;br /&gt;
        ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp1&lt;br /&gt;
    &lt;br /&gt;
        rcall   lcd_init&lt;br /&gt;
        rcall   lcd_clear&lt;br /&gt;
 &lt;br /&gt;
                                    ; Vergleichswert &lt;br /&gt;
        ldi     temp1, high( 40000 - 1 )&lt;br /&gt;
        out     OCR1AH, temp1&lt;br /&gt;
        ldi     temp1, low( 40000 - 1 )&lt;br /&gt;
        out     OCR1AL, temp1&lt;br /&gt;
                                    ; CTC Modus einschalten&lt;br /&gt;
                                    ; Vorteiler auf 1&lt;br /&gt;
        ldi     temp1, ( 1 &amp;lt;&amp;lt; WGM12 ) | ( 1 &amp;lt;&amp;lt; CS10 )&lt;br /&gt;
        out     TCCR1B, temp1&lt;br /&gt;
 &lt;br /&gt;
        ldi     temp1, 1 &amp;lt;&amp;lt; OCIE1A  ; OCIE1A: Interrupt bei Timer Compare&lt;br /&gt;
        out     TIMSK, temp1&lt;br /&gt;
 &lt;br /&gt;
        clr     Minuten             ; Die Uhr auf 0 setzen&lt;br /&gt;
        clr     Sekunden&lt;br /&gt;
        clr     Stunden&lt;br /&gt;
        clr     SubCount&lt;br /&gt;
        clr     Flag                ; Flag löschen&lt;br /&gt;
&lt;br /&gt;
        sei&lt;br /&gt;
loop:&lt;br /&gt;
        cpi     flag,0&lt;br /&gt;
        breq    loop                ; Flag im Interrupt gesetzt?&lt;br /&gt;
        ldi     flag,0              ; Flag löschen&lt;br /&gt;
  &lt;br /&gt;
        rcall   lcd_clear           ; das LCD löschen&lt;br /&gt;
        mov     temp1, Stunden      ; und die Stunden ausgeben&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
        ldi     temp1, &#039;:&#039;          ; zwischen Stunden und Minuten einen &#039;:&#039;&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
        mov     temp1, Minuten      ; dann die Minuten ausgeben&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
        ldi     temp1, &#039;:&#039;          ; und noch ein &#039;:&#039;&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
        mov     temp1, Sekunden     ; und die Sekunden&lt;br /&gt;
        rcall   lcd_number&lt;br /&gt;
&lt;br /&gt;
        rjmp    loop&lt;br /&gt;
 &lt;br /&gt;
timer1_compare:                     ; Timer 1 Output Compare Handler&lt;br /&gt;
&lt;br /&gt;
        push    temp1               ; temp 1 sichern&lt;br /&gt;
        in      temp1,sreg          ; SREG sichern&lt;br /&gt;
&lt;br /&gt;
        inc     SubCount            ; Wenn dies nicht der 100. Interrupt&lt;br /&gt;
        cpi     SubCount, 100       ; ist, dann passiert gar nichts&lt;br /&gt;
        brne    end_isr&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     SubCount            ; SubCount rücksetzen&lt;br /&gt;
        inc     Sekunden            ; plus 1 Sekunde&lt;br /&gt;
        cpi     Sekunden, 60        ; sind 60 Sekunden vergangen?&lt;br /&gt;
        brne    Ausgabe             ; wenn nicht kann die Ausgabe schon&lt;br /&gt;
                                    ; gemacht werden&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Sekunden            ; Sekunden wieder auf 0 und dafür&lt;br /&gt;
        inc     Minuten             ; plus 1 Minute&lt;br /&gt;
        cpi     Minuten, 60         ; sind 60 Minuten vergangen ?&lt;br /&gt;
        brne    Ausgabe             ; wenn nicht, -&amp;gt; Ausgabe&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Minuten             ; Minuten zurücksetzen und dafür&lt;br /&gt;
        inc     Stunden             ; plus 1 Stunde&lt;br /&gt;
        cpi     Stunden, 24         ; nach 24 Stunden, die Stundenanzeige&lt;br /&gt;
        brne    Ausgabe             ; wieder zurücksetzen&lt;br /&gt;
&lt;br /&gt;
                                    ; Überlauf&lt;br /&gt;
        clr     Stunden             ; Stunden rücksetzen&lt;br /&gt;
&lt;br /&gt;
Ausgabe:&lt;br /&gt;
        ldi     flag,1              ; Flag setzen, LCD updaten&lt;br /&gt;
&lt;br /&gt;
end_isr:&lt;br /&gt;
&lt;br /&gt;
        out     sreg,temp1          ; sreg wieder herstellen&lt;br /&gt;
        pop     temp1&lt;br /&gt;
        reti                        ; das wars. Interrupt ist fertig&lt;br /&gt;
&lt;br /&gt;
; Eine Zahl aus dem Register temp1 ausgeben&lt;br /&gt;
&lt;br /&gt;
lcd_number:&lt;br /&gt;
        push    temp2               ; register sichern,&lt;br /&gt;
                                    ; wird für Zwsichenergebnisse gebraucht     &lt;br /&gt;
        ldi     temp2, &#039;0&#039;         &lt;br /&gt;
lcd_number_10:                &lt;br /&gt;
        subi    temp1, 10           ; abzählen wieviele Zehner in&lt;br /&gt;
        brcs    lcd_number_1        ; der Zahl enthalten sind&lt;br /&gt;
        inc     temp2&lt;br /&gt;
        rjmp    lcd_number_10&lt;br /&gt;
lcd_number_1:&lt;br /&gt;
        rcall   lcd_data            ; die Zehnerstelle ausgeben&lt;br /&gt;
        subi    temp1, -10          ; 10 wieder dazuzählen, da die&lt;br /&gt;
                                    ; vorhergehende Schleife 10 zuviel&lt;br /&gt;
                                    ; abgezogen hat&lt;br /&gt;
                                    ; das Subtrahieren von -10&lt;br /&gt;
                                    ; = Addition von +10 ist ein Trick&lt;br /&gt;
                                    ; da kein addi Befehl existiert&lt;br /&gt;
        ldi     temp2, &#039;0&#039;          ; die übrig gebliebenen Einer&lt;br /&gt;
        add     temp1, temp2        ; noch ausgeben&lt;br /&gt;
        rcall   lcd_data&lt;br /&gt;
&lt;br /&gt;
        pop     temp2               ; Register wieder herstellen&lt;br /&gt;
        ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Interrupt Routine werden wieder, genauso wie vorher, die Anzahl der Interrupt Aufrufe gezählt. Beim 100. Aufruf sind daher 40.000 * 100 = 4.000.000 Takte vergangen und da der Quarz mit 4.000.000 Schwingungen in der Sekunde arbeitet, ist daher eine Sekunde vergangen. Sie wird genauso wie vorher registriert und die Uhr entsprechend hochgezählt. Wird jetzt die Uhr mit einer kommerziellen verglichen, dann fällt nach einiger Zeit auf ... Sie geht immer noch falsch! Was ist jetzt die Ursache? Die Ursache liegt in einem Problem, das nicht direkt behebbar ist. Am Quarz! Auch wenn auf dem Quarz drauf steht, dass er eine Frequenz von 4MHz hat, so stimmt das nicht exakt. Auch Quarze haben Fertigungstoleranzen verändern ihre Frequenz mit der Temperatur. Typisch liegt die Fertigungstoleranz bei +/- 100ppm = 0,01% (&#039;&#039;&#039;p&#039;&#039;&#039;arts &#039;&#039;&#039;p&#039;&#039;&#039;er &#039;&#039;&#039;m&#039;&#039;&#039;illion, Millionstel Teile), die Temperaturdrift zwischen -40 Grad und 85 Grad liegt je nach Typ in der selben Grössenordnung. Das bedeutet, dass die Uhr pro Monat um bis zu 268 Sekunden (~4 1/2 Minuten) falsch gehen kann. Diese Einflüsse auf die Quarzfrequenz sind aber messbar und per Hardware oder Software behebbar. In Uhren kommen normalerweise genauer gefertigte Uhrenquarze zum Einsatz, die vom Uhrmacher auch noch auf die exakte Frequenz abgeglichen werden (mittels Kondensatoren und Frequenzzähler). Ein Profi verwendet einen sehr genauen Frequenzzähler, womit er innerhalb weniger Sekunden die Frequenz sehr genau messen kann. Als Hobbybastler kann man die Uhr eine zeitlang (Tage, Wochen) laufen lassen und die Abweichung feststellen (z.B. exakt 20:00 Uhr zum Start der Tagsschau). Aus dieser Abweichung lässt sich dann errechnen wie schnell der Quarz wirklich schwingt. Und da dank CTC die Messperiode taktgenau eingestellt werden kann, ist es möglich diesen Frequenzfehler auszugleichen. Der genaue Vorgang ist in dem Wikiartikel [[AVR_-_Die_genaue_Sekunde_/_RTC]] beschrieben. &lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Timer|&lt;br /&gt;
zurücklink=AVR-Tutorial: Timer|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=ADC|&lt;br /&gt;
vorlink=AVR-Tutorial: ADC}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25224</id>
		<title>AVR-Tutorial: ADC</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25224"/>
		<updated>2007-12-26T11:22:26Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Zweites Beispiel angefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Was macht der ADC? ==&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht Spannungen zu messen, wird der [[ADC | &#039;&#039;&#039;A&#039;&#039;&#039;nalog &#039;&#039;&#039;D&#039;&#039;&#039;igital &#039;&#039;&#039;C&#039;&#039;&#039;onverter]] benutzt. Er konvertiert eine elektrische Spannung in eine Digitalzahl. Diese kann dann in gewohnter Weise von einem [[Mikrocontroller]] weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
== Elektronische Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Natürlich kann der ADC nicht beliebig hohe Spannungen verarbeiten. Die Grenze, bis zu der der ADC arbeitet ohne Schaden zu nehmen, liegt bei der analogen Versorgungsspannung (AVcc). Die ADC-Eingangsspannung darf diese maximal um 0.5 Volt überschreiten. Wird der Mikrocontroller also mit 5 Volt betrieben, so liegt die maximale Eingangsspannung bei ca. 5.5 Volt.&lt;br /&gt;
&lt;br /&gt;
Der Eingangswiderstand des ADC liegt in der Größenordnung von einigen Megaohm, so dass der ADC die Sgnalquelle praktisch nicht belastet. Desweiteren enthält der Mikrocontroller eine sog. &#039;&#039;&#039;Sample&amp;amp;Hold&#039;&#039;&#039; Schaltung. Dies ist wichtig, wenn sich während des Wandlungsvorgangs die Eingangsspannung verändert, da die AD-Wandlung eine bestimmte Zeit dauert. Die Sample&amp;amp;Hold-Stufe speichert zum Beginn der Wandlung die anliegende Spannung und hält sie während des Wandlungsvorgangs konstant.&lt;br /&gt;
&lt;br /&gt;
=== Beschaltung des ADC-Eingangs ===&lt;br /&gt;
&lt;br /&gt;
Um den ADC im Folgenden zu testen wird eine einfache Schaltung an den PC0-Pin des ATmega8 angeschlossen. Dies ist der ADC-Kanal 0. Bei anderen AVR-Typen liegt der entsprechende Eingang auf einem andern Pin, hier ist ein Blick ins Datenblatt angesagt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_01.gif]]&lt;br /&gt;
&lt;br /&gt;
Der Wert des [[Potentiometer]]s ist Dank des hohen Eingangswiderstandes des ADC ziemlich unkritisch. Es kann jedes Potentiometer von 1k&amp;amp;Omega; bis 1M&amp;amp;Omega; benutzt werden.&lt;br /&gt;
&lt;br /&gt;
Wenn andere Messgrößen gemessen werden sollen, so bedient man sich oft und gern des Prinzips des [[Spannungsteiler]]s. Der Sensor ist ein veränderlicher Widerstand. Zusammen mit einem zweiten, konstanten Widerstand bekannter Größe wird ein Spannungsteiler aufgebaut. Aus der Variation der durch den variablen Spannungsteiler entstehenden Spannung kann auf den Messwert zurückgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
      Vcc ----------+                Vcc ---------+&lt;br /&gt;
                    |                             |&lt;br /&gt;
                   ---                         Sensor,&lt;br /&gt;
                   | |                     der seinen Widerstand&lt;br /&gt;
                   | |                     in Abhängigkeit der&lt;br /&gt;
                   ---                     Messgröße ändert&lt;br /&gt;
                    |                             |&lt;br /&gt;
                    +------- PC0                  +-------- PC0&lt;br /&gt;
                    |                             |&lt;br /&gt;
                Sensor,                      ---&lt;br /&gt;
           der seinen Widerstand                 | |&lt;br /&gt;
           in Abhängigkeit der                   | |&lt;br /&gt;
           Messgröße ändert                      ---&lt;br /&gt;
                    |                             |&lt;br /&gt;
       GND ---------+                 GND --------+&lt;br /&gt;
&lt;br /&gt;
Die Größe des zweiten Widerstandes im Spannungsteiler richtet sich nach dem Wertebereich, in dem der Sensor seinen Wert ändert. Als Daumenregel kann man sagen, dass der Widerstand so gross sein sollte wie der Widerstand des Sensors in der Mitte des Messbereichs.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Wenn ein Temperatursensor seinen Widerstand von 0..100 Grad von 2k&amp;amp;Omega; auf 5k&amp;amp;Omega; ändert, sollte der zweite Widerstand eine Grösse von etwa (2+5)/2 = 3,5k&amp;amp;Omega; haben.&lt;br /&gt;
&lt;br /&gt;
===Referenzspannung AREF===&lt;br /&gt;
&lt;br /&gt;
Der ADC benötigt für seine Arbeit eine Referenzspannung. Dabei gibt es 2 Möglichkeiten:&lt;br /&gt;
* interne Referenzspannung&lt;br /&gt;
* externe Referenzspannung&lt;br /&gt;
&lt;br /&gt;
==== Interne Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Mittels Konfigurationsregister können beim ATmega8 verschiedene Referenzspannungen eingestellt werden. Dies umfasst die Versorungsspannung AVcc sowie eine vom AVR bereitgestellte Spannung von 2,56V (bzw. bei den neueren AVRs 1,1V, wie z.B. beim ATtiny13, ATmega48, 88, 168, ...). In beiden Fällen wird an den AREF-Pin des Prozessors ein Kondensator von 100nF als Minimalbeschaltung nach Masse angeschlossen, um die Spannung zu puffern/glätten.&lt;br /&gt;
&lt;br /&gt;
==== Externe Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Wird eine externe Referenz verwendet, so wird diese an AREF angeschlossen. Aber aufgepasst! Wenn eine Referenz in Höhe der Versorgungsspannung benutzt werden soll, so ist es besser dies über die interne Referenz zu tun. Ausser bei anderen Spannungen als 5V bzw. 2,56V gibt es eigentlich keinen Grund an AREF eine Spannungsquelle anzuschliessen. In Standardanwendungen fährt man immer besser wenn die interne Referenzspannung mit einem Kondensator an AREF benutzt wird.&lt;br /&gt;
&lt;br /&gt;
== Ein paar ADC-Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Der ADC ist ein 10-Bit ADC, d.h. er liefert Messwerte im Bereich 0 bis 1023. Liegt am Eingangskanal 0V an, so liefert der ADC einen Wert von 0. Hat die Spannung am Eingangskanal die Referenzspannung erreicht (stimmt nicht ganz), so liefert der ADC einen Wert von 1023. Unterschreitet oder überschreitet die zu messende Spannung diese Grenzen, so liefert der ADC 0 bzw. 1023. Wird die Auflösung von 10 Bit nicht benötigt, so ist es möglich die Ausgabe durch ein Konfigurationsregister so einzuschränken, dass ein leichter Zugriff auf die 8 höchstwertigen Bits möglich ist.&lt;br /&gt;
&lt;br /&gt;
Wie bei vielen analogen Schaltungen, unterliegt auch der ADC einem Rauschen. Das bedeutet, dass man nicht davon ausgehen sollte, dass der ADC bei konstanter Eingangsspannung auch immer denselben konstanten Wert ausgibt. Ein &amp;quot;Zittern&amp;quot; der niederwertigsten 2 Bits ist durchaus nicht ungewöhnlich. Besonders hervorgehoben werden soll an dieser Stelle nochmals die Qualität der Referenzspannung. Diese Qualität geht in erheblichem Maße in die Qualität der Wandelergebnisse ein. Die Beschaltung von AREF mit einem Kondensator ist die absolut notwendige Mindestbeschaltung, um eine einigermaßen akzeptable Referenzspannung zu erhalten. Reicht dies nicht aus, so kann die Qualität einer Messung durch &amp;lt;i&amp;gt;Oversampling&amp;lt;/i&amp;gt; erhöht werden. Dazu werden mehrere Messungen gemacht und deren Mittelwert gebildet.&lt;br /&gt;
&lt;br /&gt;
Oft interessiert auch der absolute Spannungspegel nicht. Im Beschaltungsbeispiel oben ist man normalerweise nicht direkt an der am Poti entstehenden Spannung interessiert. Viel mehr ist diese Spannung nur ein notwendiges Übel, um die Stellung des Potis zu bestimmen. In solchen Fällen kann die Poti-Beschaltung wie folgt abgewandelt werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_03.gif]]&lt;br /&gt;
 &lt;br /&gt;
Hier wird AREF (bei interner Referenz) als vom µC gelieferte Spannung benutzt und vom Spannungsteiler bearbeitet wieder an den µC zur Messung zurückgegeben. Dies hat den Vorteil, dass der Spannungsteiler automatisch Spannungen bis zur Höhe der Referenzspannung ausgibt, ohne dass eine externe Spannung mit AREF abgeglichen werden müsste. Selbst Schwankungen in AREF wirken sich hier nicht mehr aus, da ja das Verhältnis der Spannungsteilerspannung zu AREF immer konstant bleibt (ratiometrische Messung). Und im Grunde bestimmt der ADC ja nur dieses Verhältnis. Wird diese Variante gewählt, so muss berücksichtigt werden, dass die Ausgangsspannung an AREF nicht allzusehr belastet wird. Der Spannungsteiler muss einen Gesamtwiderstamd von deutlich über 10k&amp;amp;Omega; besitzen. Werte von 100k&amp;amp;Omega; oder höher sind anzustreben. Verwendet man anstatt AREF AVCC und schaltet auch die Referenzspannung auf AVCC um, ist die Belastung durch den Poti unkritisch, weil hier die Stromversorgung direkt zur Speisung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Ist hingegen die absolute Spannung von Interesse, so muss man darauf achten, dass ein ADC in [[Digital | digitalen]] Bereichen arbeitet ([[Quantisierung]]). An einem einfacheren Beispiel soll demonstriert werden was damit gemeint ist.&lt;br /&gt;
&lt;br /&gt;
Angenommen der ADC würde nur 5 Stufen auflösen können und AREF sei 5V:&lt;br /&gt;
&lt;br /&gt;
      Volt    Wert vom ADC&lt;br /&gt;
&lt;br /&gt;
       0 -+&lt;br /&gt;
          |         0&lt;br /&gt;
       1 -+&lt;br /&gt;
          |         1&lt;br /&gt;
       2 -+&lt;br /&gt;
          |         2&lt;br /&gt;
       3 -+&lt;br /&gt;
          |         3&lt;br /&gt;
       4 -+&lt;br /&gt;
          |         4&lt;br /&gt;
       5 -+&lt;br /&gt;
&lt;br /&gt;
Ein ADC Wert von 0 bedeutet also keineswegs, dass die zu messende Spannung exakt den Wert 0 hat. Es bedeutet lediglich, dass die Messspannung irgendwo im Bereich von 0V bis 1V liegt. Sinngemäß bedeutet daher auch das Auftreten des Maximalwertes nicht, dass die Spannung exakt AREF beträgt, sondern lediglich, dass die Messspannung sich irgendwo im Bereich der letzten Stufe (also von 4V bis 5V) bewegt.&lt;br /&gt;
&lt;br /&gt;
== Umrechnung des ADC Wertes in eine Spannung ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines &amp;quot;Bereiches&amp;quot; bestimmt sich also zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac {Referenzspannung}{Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Messwert vom ADC rechnet sich dann wie folgt in eine Spannung um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  Spannung = ADCwert \cdot \frac {Referenzspannung} {Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der ADC also mit 10 Bit an 5 V betrieben, so lauten die Umrechnungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Spannung = ADCwert \cdot \frac{5}{1024}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac{5}{1024} = 0,004883V = 4,883mV&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man genau hinsieht stellt man fest, daß sowohl die Referenzspannung als auch der Maximalwert Konstanten sind. D.h. der Quotient aus Referenzspannung und Maximalwert+1 ist konstant. Somit muss nicht immer eine Multiplikation und Division ausgeführt werden, sondern nur eine Multiplikation! Das spart viel Aufwand und Rechenzeit! Dabei kommt [[Festkommaarithmetik]] zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
== Die Steuerregister des ADC ==&lt;br /&gt;
&lt;br /&gt;
=== ADMUX ===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  | REFS1 | REFS0 | ADLAR |       |  MUX3 |  MUX2 |  MUX1 |  MUX0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* Referenzspannung &amp;lt;i&amp;gt;REFS1, REFS0&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Referenz&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;externe Referenz&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: Avcc&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;wird beim Mega8 nicht benutzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: 2.56 Volt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Ausrichtung &amp;lt;i&amp;gt;ADLAR&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADLAR&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL rechtsbündig ausgerichtet. Die 8 niederwertigsten Bits des Ergebnisses werden in ADCL abgelegt. Die verbleibenden 2 Bits des Ergebnisses werden im Register ADCH in den Bits 0 und 1 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL linksbündig ausgerichtet. Die 8 höchstwertigen Bits des Ergebnisses werden in ADCH abgelegt. Die verbleibenden 2 niederwertigen Bits werden im Register ADCL in den Bits 6 und 7 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Kanalwahl &amp;lt;i&amp;gt;MUX3, MUX2, MUX1, MUX0&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX3&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Kanal&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 0, Pin PC0&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 1, Pin PC1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 2, Pin PC2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 3, Pin PC3&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 4, Pin PC4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 5, Pin PC5&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 6, Pin ADC7 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 7, Pin ADC8 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1.23V, Vbg&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0V, GND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(*) nur in der Gehäusebauform TQFP und MLF verfügbar&lt;br /&gt;
&lt;br /&gt;
===ADCSRA===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  |  ADEN |  ADSC |  ADFR |  ADIF |  ADIE | ADPS2 | ADPS1 | ADPS0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* ADEN (ADC Enable): Mittels ADEN wird der ADC ein und ausgeschaltet. Eine 1 an dieser Bitposition schaltet den ADC ein.&lt;br /&gt;
* ADSC (ADC Start Conversion): Wird eine 1 an diese Bitposition geschrieben, so beginnt der ADC mit der Wandlung. Das Bit bleibt auf 1, solange die Wandlung im Gange ist. Wenn die Wandlung beendet ist, wird dieses Bit von der ADC Hardware wieder auf 0 gesetzt.&lt;br /&gt;
* ADFR (ADC Free Running): Wird eine 1 an ADFR geschrieben, so wird der ADC im Free Running Modus betrieben. Dabei startet der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach dem Abschluss einer Messung automatisch die nächste Messung. Die erste Messung wird ganz normal über das Setzen des &#039;&#039;&#039;ADSC&#039;&#039;&#039; Bits gestartet.&lt;br /&gt;
* ADIF (ADC Interrupt Flag): Wenn eine Messung abgeschlossen ist, wird das ADIF Bit gesetzt. Ist zusätzlich noch das &amp;lt;i&amp;gt;ADIE&amp;lt;/i&amp;gt; Bit gesetzt, so wird ein Interrupt ausgelöst und der entsprechende Interrupt Handler angesprungen.&lt;br /&gt;
* ADIE (ADC Interrupt Enable): Wird eine 1 an ADIE geschrieben, so löst der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach Beendigung einer Messung einen Interrupt aus.&lt;br /&gt;
* ADPS2, ADPS1, ADPS0 (ADC Prescaler): Mit dem Prescaler kann die ADC-Frequenz gewählt werden. Laut Datenblatt sollte diese für die optimale Auflösung zwischen 50KHz und 200kHz liegen. Ist die Wandlerfrequenz langsamer eingestellt, kann es passieren dass die eingebaute Sample &amp;amp; Hold Schaltung die Eingangsspannung nicht lange genug konstant halten kann. Ist die Frequenz aber zu schnell eingestellt, dann kann es passieren dass sich die Sample &amp;amp; Hold Schaltung nicht schnell genug an die Eingangsspannung anpassen kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Vorteiler&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;16&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;32&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;64&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;128&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Ergebnisregister ADCL und ADCH ==&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis des ADC ein 10 Bit Wert ist, passt dieser Wert naturgemäß nicht in ein einzelnes Register, das je bekanntlich nur 8 Bit breit ist. Daher wird das Ergebnis in 2 Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; abgelegt. Von den 10 Ergebnisbits sind die niederwertigsten 8 im Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; abgelegt und die noch fehlenden 2 Bits werden im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; an den niederwertigsten Bitpositionen gespeichert.&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
                             9   8       7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
Diese Zuordnung kann aber auch geändert werden: Durch setzen des &#039;&#039;&#039;ADLAR&#039;&#039;&#039; Bits im &#039;&#039;&#039;ADMUX&#039;&#039;&#039; Register wird die Ausgabe geändert zu:&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     9   8   7   6   5   4   3   2       1   0&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders dann interessant, wenn das ADC Ergebnis als 8 Bit Zahl weiterverarbeitet werden soll. In diesem Fall stehen die 8 höchstwertigen Bits bereits verarbeitungsfertig im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Beim Auslesen der ADC-Register ist zu beachten:&lt;br /&gt;
Immer zuerst &#039;&#039;&#039;ADCL&#039;&#039;&#039; und erst dann &#039;&#039;&#039;ADCH&#039;&#039;&#039; auslesen. Beim Zugriff auf &#039;&#039;&#039;ADCL&#039;&#039;&#039; wird das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register gegenüber Veränderungen vom &#039;&#039;&#039;ADC&#039;&#039;&#039; gesperrt. Erst beim nächsten Auslesen des &#039;&#039;&#039;ADCH&#039;&#039;&#039;-Registers wird diese Sperre wieder aufgehoben. Dadurch ist sichergestellt, daß die Inhalte von &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; immer aus demselben Wandlungsergebnis stammen, selbst wenn der &#039;&#039;&#039;ADC&#039;&#039;&#039; im Hintergrund selbsttätig weiterwandelt. Das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register &#039;&#039;&#039;muss&#039;&#039;&#039; ausgelesen werden!&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe als ADC-Wert ===&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm liest in einer Schleife ständig den ADC aus und verschickt des Ergebnis im Klartext (ASCII) über die [[AVR-Tutorial: UART|UART]]. Zur Verringerung des unvermeidlichen Rauschens werden 256 Messwerte herangezogen und deren Mittelwert als endgültiges Messergebnis gewertet. Dazu werden die einzelnen Messungen in den Registern temp2, temp3, temp4 als 24 Bit Zahl aufaddiert. Die Division durch 256 erfolgt dann ganz einfach dadurch, dass das Register temp2 verworfen wird und die Register temp3 und temp4 als 16 Bit Zahl aufgefasst werden. Eine Besonderheit ist noch, dass je nach dem Wert in temp2 die 16 Bit Zahl in temp3 und temp4 noch aufgerundet wird: Enthält temp2 einen Wert größer als 128, dann wird zur 16 Bit Zahl in temp3/temp4 noch 1 dazu addiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r16         ; allgemeines temp Register, zur krufristigen Verwendung&lt;br /&gt;
.def temp2     = r17         ; Register für 24 Bit Addition, Lowest Byte&lt;br /&gt;
.def temp3     = r18         ; Register für 24 Bit Addition, Middle Byte&lt;br /&gt;
.def temp4     = r19         ; Register für 24 Bit Addition, Highest Byte&lt;br /&gt;
.def adlow     = r20         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def adhigh    = r21         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def messungen = r22         ; Schleifenzähler für die Messungen&lt;br /&gt;
.def ztausend  = r23         ; Zehntausenderstelle des ADC Wertes&lt;br /&gt;
.def tausend   = r24         ; Tausenderstelle des ADC Wertes&lt;br /&gt;
.def hundert   = r25         ; Hunderterstelle des ADC Wertes&lt;br /&gt;
.def zehner    = r26         ; Zehnerstelle des ADC Wertes&lt;br /&gt;
.def zeichen   = r27         ; Zeichen zur Ausgabe auf den UART&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programm los&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(RAMEND)                  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
;UART Initalisierung&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(UBRR_VAL)                ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, TXEN                         ; TX einschalten&lt;br /&gt;
 &lt;br /&gt;
; ADC initialisieren: Single Conversion, Vorteiler 128&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;REFS0)                   ; Kanal 0, interne Referenzspannung 5V&lt;br /&gt;
    out     ADMUX, temp1&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
    out     ADCSRA, temp1&lt;br /&gt;
 &lt;br /&gt;
Main:&lt;br /&gt;
    clr     temp1&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
&lt;br /&gt;
    ldi     messungen, 0        ; 256 Schleifendurchläufe&lt;br /&gt;
 &lt;br /&gt;
; neuen ADC-Wert lesen  (Schleife - 256 mal)&lt;br /&gt;
&lt;br /&gt;
sample_adc:&lt;br /&gt;
    sbi     ADCSRA, ADSC        ; den ADC starten&lt;br /&gt;
 &lt;br /&gt;
wait_adc:&lt;br /&gt;
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht&lt;br /&gt;
    rjmp    wait_adc&lt;br /&gt;
 &lt;br /&gt;
; ADC einlesen:&lt;br /&gt;
&lt;br /&gt;
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen&lt;br /&gt;
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte&lt;br /&gt;
 &lt;br /&gt;
; alle 256 ADC-Werte addieren&lt;br /&gt;
; dazu wird mit den Registern temp4, temp3 und temp2 ein&lt;br /&gt;
; 24-Bit breites Akkumulationsregister gebildet, in dem&lt;br /&gt;
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden&lt;br /&gt;
&lt;br /&gt;
    add     temp2, adlow        ; addieren&lt;br /&gt;
    adc     temp3, adhigh       ; addieren über Carry&lt;br /&gt;
    adc     temp4, temp1        ; addieren über Carry, temp1 enthält 0&lt;br /&gt;
    dec     messungen           ; Schleifenzähler MINUS 1&lt;br /&gt;
    brne    sample_adc          ; wenn noch keine 256 ADC Werte -&amp;gt; nächsten Wert einlesen&lt;br /&gt;
 &lt;br /&gt;
; Aus den 256 Werten den Mittelwert berechnen&lt;br /&gt;
; Bei 256 Werten ist das ganz einfach: Das niederwertigste Byte&lt;br /&gt;
; (im Register temp2) fällt einfach weg&lt;br /&gt;
;&lt;br /&gt;
; allerdings wird der Wert noch gerundet&lt;br /&gt;
&lt;br /&gt;
    cpi     temp2,128           ; &amp;quot;Kommastelle&amp;quot; kleiner als 128 ?&lt;br /&gt;
    brlo    no_round            ; ist kleiner ==&amp;gt; Sprung&lt;br /&gt;
 &lt;br /&gt;
; Aufrunden&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
 &lt;br /&gt;
no_round:&lt;br /&gt;
&lt;br /&gt;
;   Ergebnis nach adlow und adhigh kopieren&lt;br /&gt;
;   damit die temp Register frei werden&lt;br /&gt;
&lt;br /&gt;
    mov     adlow, temp3&lt;br /&gt;
    mov     adhigh, temp4&lt;br /&gt;
 &lt;br /&gt;
;in ASCII umwandeln&lt;br /&gt;
&lt;br /&gt;
    ldi     ztausend, -1 + &#039;0&#039;&lt;br /&gt;
_a6ser:&lt;br /&gt;
    inc     ztausend&lt;br /&gt;
    subi    adlow, low(10000)   ; -10,000&lt;br /&gt;
    sbci    adhigh, high(10000)&lt;br /&gt;
    brcc    _a6ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     tausend, 10 + &#039;0&#039;&lt;br /&gt;
_a7ser:&lt;br /&gt;
    dec     tausend&lt;br /&gt;
    subi    adlow, low(-1000)   ; +1000&lt;br /&gt;
    sbci    adhigh, high(-1000)&lt;br /&gt;
    brcs    _a7ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     hundert, -1 + &#039;0&#039;&lt;br /&gt;
_a8ser:&lt;br /&gt;
    inc     hundert&lt;br /&gt;
    subi    adlow, low(100)    ; -100&lt;br /&gt;
    sbci    adhigh, high(100)&lt;br /&gt;
    brcc    _a8ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     zehner, 10 + &#039;0&#039;&lt;br /&gt;
_a9ser:&lt;br /&gt;
    dec     zehner&lt;br /&gt;
    subi    adlow, -10          ; +10&lt;br /&gt;
    brcs    _a9ser&lt;br /&gt;
    subi    adlow,-&#039;0&#039;&lt;br /&gt;
 &lt;br /&gt;
;an UART Senden&lt;br /&gt;
&lt;br /&gt;
    mov     zeichen, ztausend   ; Zehntausender Stelle&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, tausend    ; Tausender Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, hundert    ; Hunderter Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, zehner     ; Zehner Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, adlow      ; Einer Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 13         ; CR&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 10         ; LF&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
 &lt;br /&gt;
    rjmp    Main&lt;br /&gt;
 &lt;br /&gt;
transmit:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    transmit&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausgabe als Spannungswert ===&lt;br /&gt;
&lt;br /&gt;
Das zweite Beispiel ist schon um einiges größer. Hier wird der gemittelte ADC-Wert in eine Spannung umgerechnet. Dazu wird [[Festkommaarithmetik]] verwendet. Die Daten sind in diesem Fall&lt;br /&gt;
&lt;br /&gt;
* Referenzspannung : 5V&lt;br /&gt;
* alte Auflösung   : 5V / 1024 = 4,8828125mV&lt;br /&gt;
* neue Auflösung   : 1mV&lt;br /&gt;
&lt;br /&gt;
-&amp;gt; Faktor = 4,8828125mV / 1mV = 4,8828125&lt;br /&gt;
&lt;br /&gt;
Der Faktor wird dreimal mit 10 multipliziert und das Ergebnis auf 4883 gerundet. Die neue Auflösung wird dreimal durch 10 dividiert und beträgt 1&amp;amp;mu;V. Der relative Fehler beträgt&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt; F_r = \frac {4883}{4882,8125}-1 = 0,00384% = \frac {1}{26042}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Fehler ist absolut vernachlässigbar. Nach der Multiplikation des ADC-Wertes mit 4883 liegt die gemessene Spannung in der Einheit &amp;amp;mu;V vor. Vorsicht! Das ist &#039;&#039;&#039;nicht&#039;&#039;&#039; die reale [[Auflösung und Genauigkeit]], nur rein mathematisch bedingt. Für maximale Genauigkeit sollte man die Versorgungsspannung AVCC, welche hier gleichzeitig als Referenzspannung dient, exakt messen, die Rechnung nachvollziehen und den Wert im Quelltext eintragen. Damit führt man eine einfach Einpunktkalibrierung durch.&lt;br /&gt;
&lt;br /&gt;
Da das Programm schon um einiges größer und komplexer ist, wurde es im Vergleich zur Vorgängerversion geändert. Die Multiplikation sowie die Umwandung der Zahl in einen ASCII-String sind als Unterprogramme geschrieben, dadurch erhält man wesentlich mehr Überblick im Hauptprogramm und die Wiederverwendung in anderen Programmen vereinfacht sich. Ausserdem wird der String im RAM gespeichert und nicht mehr in CPU-Registern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r16         ; allgemeines temp Register, zur kurzfristige Verwendung&lt;br /&gt;
.def temp2     = r17         ; Register für 24 Bit Addition, Lowest Byte&lt;br /&gt;
.def temp3     = r18         ; Register für 24 Bit Addition, Middle Byte&lt;br /&gt;
.def temp4     = r19         ; Register für 24 Bit Addition, Highest Byte&lt;br /&gt;
.def adlow     = r20         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def adhigh    = r21         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def messungen = r22         ; Schleifenzähler für die Messungen&lt;br /&gt;
.def zeichen   = r23         ; Zeichen zur Ausgabe auf den UART&lt;br /&gt;
.def temp5     = r24&lt;br /&gt;
.def temp6     = r25&lt;br /&gt;
&lt;br /&gt;
; Faktor für Umrechung des ADC-Wertes in Spannung&lt;br /&gt;
; = (Referenzspannung / 1024 ) * 100000&lt;br /&gt;
; = 5V / 1024 * 1.000.000&lt;br /&gt;
.equ Faktor = 4883&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
; RAM&lt;br /&gt;
.dseg&lt;br /&gt;
.org 0x60&lt;br /&gt;
Puffer: .byte 10&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm los&lt;br /&gt;
.cseg&lt;br /&gt;
.org 0&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(RAMEND)                  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
;UART Initalisierung&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(UBRR_VAL)                ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, TXEN                         ; TX einschalten&lt;br /&gt;
 &lt;br /&gt;
; ADC initialisieren: Single Conversion, Vorteiler 128&lt;br /&gt;
; Kanal 0, interne Referenzspannung AVCC&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;REFS0)                   &lt;br /&gt;
    out     ADMUX, temp1&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
    out     ADCSRA, temp1&lt;br /&gt;
 &lt;br /&gt;
Hauptschleife:&lt;br /&gt;
    clr     temp1&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
&lt;br /&gt;
    ldi     messungen, 0        ; 256 Schleifendurchläufe&lt;br /&gt;
 &lt;br /&gt;
; neuen ADC-Wert lesen  (Schleife - 256 mal)&lt;br /&gt;
&lt;br /&gt;
adc_messung:&lt;br /&gt;
    sbi     ADCSRA, ADSC        ; den ADC starten&lt;br /&gt;
 &lt;br /&gt;
adc_warten:&lt;br /&gt;
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht&lt;br /&gt;
    rjmp    adc_warten&lt;br /&gt;
 &lt;br /&gt;
; ADC einlesen:&lt;br /&gt;
&lt;br /&gt;
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen&lt;br /&gt;
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte&lt;br /&gt;
 &lt;br /&gt;
; alle 256 ADC-Werte addieren&lt;br /&gt;
; dazu wird mit den Registern temp4, temp3 und temp2 ein&lt;br /&gt;
; 24-Bit breites Akkumulationsregister gebildet, in dem&lt;br /&gt;
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden&lt;br /&gt;
&lt;br /&gt;
    add     temp2, adlow        ; addieren&lt;br /&gt;
    adc     temp3, adhigh       ; addieren über Carry&lt;br /&gt;
    adc     temp4, temp1        ; addieren über Carry, temp1 enthält 0&lt;br /&gt;
    dec     messungen           ; Schleifenzähler MINUS 1&lt;br /&gt;
    brne    adc_messung         ; wenn noch keine 256 ADC Werte -&amp;gt; nächsten Wert einlesen&lt;br /&gt;
 &lt;br /&gt;
; Aus den 256 Werten den Mittelwert berechnen&lt;br /&gt;
; Bei 256 Werten ist das ganz einfach: Das niederwertigste Byte&lt;br /&gt;
; (im Register temp2) fällt einfach weg&lt;br /&gt;
;&lt;br /&gt;
; allerdings wird der Wert noch gerundet&lt;br /&gt;
&lt;br /&gt;
    cpi     temp2,128           ; &amp;quot;Kommastelle&amp;quot; kleiner als 128 ?&lt;br /&gt;
    brlo    nicht_runden        ; ist kleiner ==&amp;gt; Sprung&lt;br /&gt;
 &lt;br /&gt;
; Aufrunden&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
 &lt;br /&gt;
nicht_runden:&lt;br /&gt;
&lt;br /&gt;
;   Ergebnis nach adlow und adhigh kopieren&lt;br /&gt;
;   damit die temp Register frei werden&lt;br /&gt;
&lt;br /&gt;
    mov     adlow, temp3&lt;br /&gt;
    mov     adhigh, temp4&lt;br /&gt;
&lt;br /&gt;
; in Spannung umrechnen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp5,low(Faktor)&lt;br /&gt;
    ldi     temp6,high(Faktor)&lt;br /&gt;
    rcall   mul16x16&lt;br /&gt;
&lt;br /&gt;
; in ASCII umwandeln&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL, low(Puffer)&lt;br /&gt;
    ldi     ZH, high(Puffer)&lt;br /&gt;
    rcall   Int_to_ASCII&lt;br /&gt;
 &lt;br /&gt;
;an UART Senden&lt;br /&gt;
&lt;br /&gt;
    ldi     ZL, low(Puffer+3)&lt;br /&gt;
    ldi     ZH, high(Puffer+3)&lt;br /&gt;
    ldi     temp1, 1&lt;br /&gt;
    rcall   sende_zeichen       ; eine Vorkommastelle ausgeben&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, &#039;,&#039;        ; Komma ausgeben&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, 3            ; Drei Nachkommastellen ausgeben&lt;br /&gt;
    rcall   sende_zeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, &#039;V&#039;        ; Volt Zeichen ausgeben&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, 10         ; New Line Steuerzeichen&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    ldi     zeichen, 13         ; Carrige Return Steuerzeichen&lt;br /&gt;
    rcall   sende_einzelzeichen&lt;br /&gt;
&lt;br /&gt;
    rjmp    Hauptschleife&lt;br /&gt;
&lt;br /&gt;
; Ende des Hauptprogramms&lt;br /&gt;
&lt;br /&gt;
; Unterprogramme&lt;br /&gt;
 &lt;br /&gt;
 ; ein Zeichen per UART senden&lt;br /&gt;
&lt;br /&gt;
sende_einzelzeichen:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    sende_einzelzeichen&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; mehrere Zeichen ausgeben, welche durch Z adressiert werden&lt;br /&gt;
; Anzahl in temp1&lt;br /&gt;
&lt;br /&gt;
sende_zeichen:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    sende_zeichen&lt;br /&gt;
    ld      zeichen, Z+         ; Zeichen laden&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    dec     temp1&lt;br /&gt;
    brne    sende_zeichen&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; 32 Bit Zahl in ASCII umwandeln&lt;br /&gt;
; Zahl liegt in temp1..4&lt;br /&gt;
; Ergebnis ist ein 10stelliger ASCII String, welcher im SRAM abgelegt wird&lt;br /&gt;
; Adressierung über Z Pointer&lt;br /&gt;
&lt;br /&gt;
Int_to_ASCII:&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a1ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,BYTE1(1000000000) ; - 1.000.000.000&lt;br /&gt;
    sbci    temp2,BYTE2(1000000000)&lt;br /&gt;
    sbci    temp3,BYTE3(1000000000)&lt;br /&gt;
    sbci    temp4,BYTE4(1000000000)&lt;br /&gt;
    brcc    _a1ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a2ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,BYTE1(-100000000) ; + 100.000.000&lt;br /&gt;
    sbci    temp2,BYTE2(-100000000)&lt;br /&gt;
    sbci    temp3,BYTE3(-100000000)&lt;br /&gt;
    sbci    temp4,BYTE4(-100000000)&lt;br /&gt;
    brcs    _a2ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a3ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(10000000)     ; - 10.000.000&lt;br /&gt;
    sbci    temp2,high(10000000)&lt;br /&gt;
    sbci    temp3,BYTE3(10000000)&lt;br /&gt;
    brcc    _a3ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a4ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-1000000)     ; + 1.000.000&lt;br /&gt;
    sbci    temp2,high(-1000000)&lt;br /&gt;
    sbci    temp3,BYTE3(-1000000)&lt;br /&gt;
    brcs    _a4ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a5ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(100000)       ; -100.000&lt;br /&gt;
    sbci    temp2,high(100000)&lt;br /&gt;
    sbci    temp3,BYTE3(100000)&lt;br /&gt;
    brcc    _a5ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a6ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-10000)       ; +10,000&lt;br /&gt;
    sbci    temp2,high(-10000)&lt;br /&gt;
    sbci    temp3,BYTE3(-10000)&lt;br /&gt;
    brcs    _a6ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern &lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a7ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1,low(1000)         ; -1000&lt;br /&gt;
    sbci    temp2,high(1000)&lt;br /&gt;
    brcc    _a7ser&lt;br /&gt;
 &lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a8ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1,low(-100)         ; +100&lt;br /&gt;
    sbci    temp2,high(-100)&lt;br /&gt;
    brcs    _a8ser&lt;br /&gt;
 &lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, -1 + &#039;0&#039;&lt;br /&gt;
_a9ser:&lt;br /&gt;
    inc     temp5&lt;br /&gt;
    subi    temp1, 10               ; -10&lt;br /&gt;
    brcc    _a9ser&lt;br /&gt;
    &lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ldi     temp5, 10 + &#039;0&#039;&lt;br /&gt;
_a10ser:&lt;br /&gt;
    dec     temp5&lt;br /&gt;
    subi    temp1, -1               ; +1&lt;br /&gt;
    brcs    _a10ser&lt;br /&gt;
&lt;br /&gt;
    st      Z+,temp5                ; im Puffer speichern&lt;br /&gt;
    ret&lt;br /&gt;
&lt;br /&gt;
; 16 Bit Wert in Spannung umrechnen&lt;br /&gt;
;&lt;br /&gt;
; = 16Bitx16Bit=32 Bit Multiplikation&lt;br /&gt;
; = vier 8x8 Bit Multiplikationen&lt;br /&gt;
;&lt;br /&gt;
; adlow/adhigh * temp5/temp6&lt;br /&gt;
&lt;br /&gt;
mul16x16:&lt;br /&gt;
    push    zeichen&lt;br /&gt;
    clr     temp1                   ; 32 Bit Akku löschen&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
    clr     zeichen                 ; Null, für Carry-Addition&lt;br /&gt;
&lt;br /&gt;
    mul     adlow, temp5            ; erste Multiplikation&lt;br /&gt;
    add     temp1, r0               ; und akkumulieren&lt;br /&gt;
    adc     temp2, r1&lt;br /&gt;
&lt;br /&gt;
    mul     adhigh, temp5           ; zweite Multiplikation&lt;br /&gt;
    add     temp2, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp3, r1&lt;br /&gt;
&lt;br /&gt;
    mul     adlow, temp6            ; dritte Multiplikation&lt;br /&gt;
    add     temp2, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp3, r1&lt;br /&gt;
    adc     temp4, zeichen          ; carry addieren&lt;br /&gt;
&lt;br /&gt;
    mul     adhigh, temp6           ; vierte Multiplikation&lt;br /&gt;
    add     temp3, r0               ; und gewichtet akkumlieren&lt;br /&gt;
    adc     temp4, r1&lt;br /&gt;
&lt;br /&gt;
    pop     zeichen&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Uhr|&lt;br /&gt;
zurücklink=AVR-Tutorial: Uhr|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Tasten|&lt;br /&gt;
vorlink=AVR-Tutorial: Tasten}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25221</id>
		<title>AVR-Tutorial: ADC</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_ADC&amp;diff=25221"/>
		<updated>2007-12-25T22:37:22Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Kleine Korrekturen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Was macht der ADC? ==&lt;br /&gt;
&lt;br /&gt;
Wenn es darum geht Spannungen zu messen, wird der [[ADC | &#039;&#039;&#039;A&#039;&#039;&#039;nalog &#039;&#039;&#039;D&#039;&#039;&#039;igital &#039;&#039;&#039;C&#039;&#039;&#039;onverter]] benutzt. Er konvertiert eine elektrische Spannung in eine Digitalzahl. Diese kann dann in gewohnter Weise von einem [[Mikrocontroller]] weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
== Elektronische Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Natürlich kann der ADC nicht beliebig hohe Spannungen verarbeiten. Die Grenze, bis zu der der ADC arbeitet ohne Schaden zu nehmen, liegt bei der analogen Versorgungsspannung (AVcc). Die ADC-Eingangsspannung darf diese maximal um 0.5 Volt überschreiten. Wird der Mikrocontroller also mit 5 Volt betrieben, so liegt die maximale Eingangsspannung bei ca. 5.5 Volt.&lt;br /&gt;
&lt;br /&gt;
Der Eingangswiderstand des ADC liegt in der Größenordnung von einigen Megaohm, so dass der ADC die Sgnalquelle praktisch nicht belastet. Desweiteren enthält der Mikrocontroller eine sog. &#039;&#039;&#039;Sample&amp;amp;Hold&#039;&#039;&#039; Schaltung. Dies ist wichtig, wenn sich während des Wandlungsvorgangs die Eingangsspannung verändert, da die AD-Wandlung eine bestimmte Zeit dauert. Die Sample&amp;amp;Hold-Stufe speichert zum Beginn der Wandlung die anliegende Spannung und hält sie während des Wandlungsvorgangs konstant.&lt;br /&gt;
&lt;br /&gt;
=== Beschaltung des ADC-Eingangs ===&lt;br /&gt;
&lt;br /&gt;
Um den ADC im Folgenden zu testen wird eine einfache Schaltung an den PC0-Pin des ATmega8 angeschlossen. Dies ist der ADC-Kanal 0. Bei anderen AVR-Typen liegt der entsprechende Eingang auf einem andern Pin, hier ist ein Blick ins Datenblatt angesagt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_01.gif]]&lt;br /&gt;
&lt;br /&gt;
Der Wert des [[Potentiometer]]s ist Dank des hohen Eingangswiderstandes des ADC ziemlich unkritisch. Es kann jedes Potentiometer von 1k&amp;amp;Omega; bis 1M&amp;amp;Omega; benutzt werden.&lt;br /&gt;
&lt;br /&gt;
Wenn andere Messgrößen gemessen werden sollen, so bedient man sich oft und gerne des Prinzips des [[Spannungsteiler]]s. Der Sensor ist ein veränderlicher Widerstand. Zusammen mit einem zweiten, konstanten Widerstand bekannter Größe wird ein Spannungsteiler aufgebaut. Aus der Variation der durch den variablen Spannungsteiler entstehenden Spannung kann auf den Messwert zurückgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
      Vcc ----------+                Vcc ---------+&lt;br /&gt;
                    |                             |&lt;br /&gt;
                   ---                         Sensor,&lt;br /&gt;
                   | |                     der seinen Widerstand&lt;br /&gt;
                   | |                     in Abhängigkeit der&lt;br /&gt;
                   ---                     Messgröße ändert&lt;br /&gt;
                    |                             |&lt;br /&gt;
                    +------- PC0                  +-------- PC0&lt;br /&gt;
                    |                             |&lt;br /&gt;
                Sensor,                      ---&lt;br /&gt;
           der seinen Widerstand                 | |&lt;br /&gt;
           in Abhängigkeit der                   | |&lt;br /&gt;
           Messgröße ändert                      ---&lt;br /&gt;
                    |                             |&lt;br /&gt;
       GND ---------+                 GND --------+&lt;br /&gt;
&lt;br /&gt;
Die Größe des zweiten Widerstandes im Spannungsteiler richtet sich nach dem Wertebereich, in dem der Sensor seinen Wert ändert. Als Daumenregel kann man sagen, dass der Widerstand so gross sein sollte wie der Widertand des Sensors in der Mitte des Messbereichs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Wenn ein Temperatursensor seinen Widerstand von 0..100 Grad von 2k&amp;amp;Omega; auf 5k&amp;amp;Omega; ändert, sollte der zweite Widerstand eine Grösse von etwa (2+5)/2 = 3,5k&amp;amp;Omega; haben.&lt;br /&gt;
&lt;br /&gt;
===Referenzspannung AREF===&lt;br /&gt;
&lt;br /&gt;
Der ADC benötigt für seine Arbeit eine Referenzspannung. Dabei gibt es 2 Möglichkeiten:&lt;br /&gt;
* interne Referenzspannung&lt;br /&gt;
* externe Referenzspannung&lt;br /&gt;
&lt;br /&gt;
==== Interne Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Mittels Konfigurationsregister können beim ATmega8 verschiedene Referenzspannungen eingestellt werden. Dies umfasst die Versorungsspannung AVcc sowie eine vom AVR bereitgestellte Spannung von 2,56V (bzw. bei den neueren AVRs 1,1V, wie z.B. beim ATtiny13, ATmega48, 88, 168, ...). In beiden Fällen wird an den AREF-Pin des Prozessors ein Kondensator von 100nF als Minimalbeschaltung nach Masse angeschlossen, um die Spannung zu puffern/glätten.&lt;br /&gt;
&lt;br /&gt;
==== Externe Referenzspannung ====&lt;br /&gt;
&lt;br /&gt;
Wird eine externe Referenz verwendet, so wird diese an AREF angeschlossen. Aber aufgepasst! Wenn eine Referenz in Höhe der Versorgungsspannung benutzt werden soll, so ist es besser dies über die interne Referenz zu tun. Ausser bei anderen Spannungen als 5V bzw. 2,56V gibt es eigentlich keinen Grund an AREF eine Spannungsquelle anzuschliessen. In Standardanwendungen fährt man immer besser wenn die interne Referenzspannung mit einem Kondensator an AREF benutzt wird.&lt;br /&gt;
&lt;br /&gt;
== Ein paar ADC-Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
Der ADC ist ein 10-Bit ADC, d.h. er liefert Messwerte im Bereich 0 bis 1023. Liegt am Eingangskanal 0V an, so liefert der ADC einen Wert von 0. Hat die Spannung am Eingangskanal die Referenzspannung erreicht (stimmt nicht ganz), so liefert der ADC einen Wert von 1023. Unterschreitet oder überschreitet die zu messende Spannung diese Grenzen, so liefert der ADC 0 bzw. 1023. Wird die Auflösung von 10 Bit nicht benötigt, so ist es möglich die Ausgabe durch ein Konfigurationsregister so einzuschränken, dass ein leichter Zugriff auf die 8 höchstwertigen Bits möglich ist.&lt;br /&gt;
&lt;br /&gt;
Wie bei vielen analogen Schaltungen, unterliegt auch der ADC einem Rauschen. Das bedeutet, dass man nicht davon ausgehen sollte, dass der ADC bei konstanter Eingangsspannung auch immer denselben konstanten Wert ausgibt. Ein &amp;quot;Zittern&amp;quot; der niederwertigsten 2 Bits ist durchaus nicht ungewöhnlich. Besonders hervorgehoben werden soll an dieser Stelle nochmals die Qualität der Referenzspannung. Diese Qualität geht in erheblichem Maße in die Qualität der Wandelergebnisse ein. Die Beschaltung von AREF mit einem Kondensator ist die absolut notwendige Mindestbeschaltung, um eine einigermaßen akzeptable Referenzspannung zu erhalten. Reicht dies nicht aus, so kann die Qualität einer Messung durch &amp;lt;i&amp;gt;Oversampling&amp;lt;/i&amp;gt; erhöht werden. Dazu werden mehrere Messungen gemacht und deren Mittelwert gebildet.&lt;br /&gt;
&lt;br /&gt;
Oft interessiert auch der absolute Spannungspegel nicht. Im Beschaltungsbeispiel oben ist man normalerweise nicht direkt an der am Poti entstehenden Spannung interessiert. Viel mehr ist diese Spannung nur ein notwendiges Übel, um die Stellung des Potis zu bestimmen. In solchen Fällen kann die Poti-Beschaltung wie folgt abgewandelt werden:&lt;br /&gt;
&lt;br /&gt;
[[Bild:Tut_ADC_03.gif]]&lt;br /&gt;
 &lt;br /&gt;
Hier wird AREF (bei interner Referenz) als vom µC gelieferte Spannung benutzt und vom Spannungsteiler bearbeitet wieder an den µC zur Messung zurückgegeben. Dies hat den Vorteil, dass der Spannungsteiler automatisch Spannungen bis zur Höhe der Referenzspannung ausgibt, ohne dass eine externe Spannung mit AREF abgeglichen werden müsste. Selbst Schwankungen in AREF wirken sich hier nicht mehr aus, da ja das Verhältnis der Spannungsteilerspannung zu AREF immer konstant bleibt (ratiometrische Messung). Und im Grunde bestimmt der ADC ja nur dieses Verhältnis. Wird diese Variante gewählt, so muss berücksichtigt werden, dass die Ausgangsspannung an AREF nicht allzusehr belastet wird. Der Spannungsteiler muss einen Gesamtwiderstamd von deutlich über 10k&amp;amp;Omega; besitzen. Werte von 100k&amp;amp;Omega; oder höher sind anzustreben. Verwendet man anstatt AREF AVCC und schaltet auch die Referenzspannung auf AVCC um, ist die Belastung durch den Poti unkritisch, weil hier die Stromversorgung direkt zur Speisung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Ist hingegen die absolute Spannung von Interesse, so muss man darauf achten, dass ein ADC in [[Digital | digitalen]] Bereichen arbeitet ([[Quantisierung]]). An einem einfacheren Beispiel soll demonstriert werden was damit gemeint ist.&lt;br /&gt;
&lt;br /&gt;
Angenommen der ADC würde nur 5 Stufen auflösen können und AREF sei 5V:&lt;br /&gt;
&lt;br /&gt;
      Volt    Wert vom ADC&lt;br /&gt;
&lt;br /&gt;
       0 -+&lt;br /&gt;
          |         0&lt;br /&gt;
       1 -+&lt;br /&gt;
          |         1&lt;br /&gt;
       2 -+&lt;br /&gt;
          |         2&lt;br /&gt;
       3 -+&lt;br /&gt;
          |         3&lt;br /&gt;
       4 -+&lt;br /&gt;
          |         4&lt;br /&gt;
       5 -+&lt;br /&gt;
&lt;br /&gt;
Ein ADC Wert von 0 bedeutet also keineswegs, dass die zu messende Spannung exakt den Wert 0 hat. Es bedeutet lediglich, dass die Messspannung irgendwo im Bereich von 0V bis 1V liegt. Sinngemäß bedeutet daher auch das Auftreten des Maximalwertes nicht, dass die Spannung exakt AREF beträgt, sondern lediglich, dass die Messspannung sich irgendwo im Bereich der letzten Stufe (also von 4V bis 5V) bewegt.&lt;br /&gt;
&lt;br /&gt;
== Umrechnung des ADC Wertes in eine Spannung ==&lt;br /&gt;
&lt;br /&gt;
Die Größe eines &amp;quot;Bereiches&amp;quot; bestimmt sich also zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac {Referenzspannung}{Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Messwert vom ADC rechnet sich dann wie folgt in eine Spannung um:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
  Spannung = \frac {Messwert * Referenzspannung} {Maximalwert + 1}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird der ADC also mit 10 Bit an 5 V betrieben, so lauten die Umrechnungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Spannung = \frac{Messwert * 5}{1024}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
   Bereichsbreite = \frac{5}{1024} = 0.00488 V = 4.88 mV&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man genau hinsieht stellt man fest, daß sowohl die Referenzspannung als auch der Maximalwert Konstanten sind. D.h. der Quotient aus Referenzspannung und Maximalwert+1 ist konstant. Somit muss nicht immer eine Multiplikation und Division ausgeführt werden, sondern nur eine Multiplikation! Das spart viel Aufwand und Rechenzeit! Dabei kommt [[Festkommaarithmetik]] zum Einsatz.&lt;br /&gt;
&lt;br /&gt;
== Die Steuerregister des ADC ==&lt;br /&gt;
&lt;br /&gt;
=== ADMUX ===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  | REFS1 | REFS0 | ADLAR |       |  MUX3 |  MUX2 |  MUX1 |  MUX0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* Referenzspannung &amp;lt;i&amp;gt;REFS1, REFS0&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;REFS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Referenz&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;externe Referenz&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: Avcc&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;wird beim Mega8 nicht benutzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;interne Referenz: 2.56 Volt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Ausrichtung &amp;lt;i&amp;gt;ADLAR&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADLAR&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL rechtsbündig ausgerichtet. Die 8 niederwertigsten Bits des Ergebnisses werden in ADCL abgelegt. Die verbleibenden 2 Bits des Ergebnisses werden im Register ADCH in den Bits 0 und 1 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Das Ergebnis wird in den Registern ADCH/ADCL linksbündig ausgerichtet. Die 8 höchstwertigen Bits des Ergebnisses werden in ADCH abgelegt. Die verbleibenden 2 niederwertigen Bits werden im Register ADCL in den Bits 6 und 7 abgelegt.&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Kanalwahl &amp;lt;i&amp;gt;MUX3, MUX2, MUX1, MUX0&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX3&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;MUX0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Kanal&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 0, Pin PC0&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 1, Pin PC1&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 2, Pin PC2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 3, Pin PC3&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 4, Pin PC4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 5, Pin PC5&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 6, Pin ADC7 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Kanal 7, Pin ADC8 (*)&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1.23V, Vbg&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0V, GND&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(*) nur in der Gehäusebauform TQFP und MLF verfügbar&lt;br /&gt;
&lt;br /&gt;
===ADCSRA===&lt;br /&gt;
&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
  |  ADEN |  ADSC |  ADFR |  ADIF |  ADIE | ADPS2 | ADPS1 | ADPS0 |&lt;br /&gt;
  +-------+-------+-------+-------+-------+-------+-------+-------+&lt;br /&gt;
&lt;br /&gt;
* ADEN (ADC Enable): Mittels ADEN wird der ADC ein und ausgeschaltet. Eine 1 an dieser Bitposition schaltet den ADC ein.&lt;br /&gt;
* ADSC (ADC Start Conversion): Wird eine 1 an diese Bitposition geschrieben, so beginnt der ADC mit der Wandlung. Das Bit bleibt auf 1, solange die Wandlung im Gange ist. Wenn die Wandlung beendet ist, wird dieses Bit von der ADC Hardware wieder auf 0 gesetzt.&lt;br /&gt;
* ADFR (ADC Free Running): Wird eine 1 an ADFR geschrieben, so wird der ADC im Free Running Modus betrieben. Dabei startet der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach dem Abschluss einer Messung automatisch die nächste Messung. Die erste Messung wird ganz normal über das Setzen des &#039;&#039;&#039;ADSC&#039;&#039;&#039; Bits gestartet.&lt;br /&gt;
* ADIF (ADC Interrupt Flag): Wenn eine Messung abgeschlossen ist, wird das ADIF Bit gesetzt. Ist zusätzlich noch das &amp;lt;i&amp;gt;ADIE&amp;lt;/i&amp;gt; Bit gesetzt, so wird ein Interrupt ausgelöst und der entsprechende Interrupt Handler angesprungen.&lt;br /&gt;
* ADIE (ADC Interrupt Enable): Wird eine 1 an ADIE geschrieben, so löst der &#039;&#039;&#039;ADC&#039;&#039;&#039; nach Beendigung einer Messung einen Interrupt aus.&lt;br /&gt;
* ADPS2, ADPS1, ADPS0 (ADC Prescaler): Mit dem Prescaler kann die ADC-Frequenz gewählt werden. Laut Datenblatt sollte diese für die optimale Auflösung zwischen 50KHz und 200kHz liegen. Ist die Wandlerfrequenz langsamer eingestellt, kann es passieren dass die eingebaute Sample &amp;amp; Hold Schaltung die Eingangsspannung nicht lange genug konstant halten kann. Ist die Frequenz aber zu schnell eingestellt, dann kann es passieren dass sich die Sample &amp;amp; Hold Schaltung nicht schnell genug an die Eingangsspannung anpassen kann.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS2&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS1&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;ADPS0&amp;lt;/th&amp;gt;&amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Vorteiler&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;2&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;4&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;8&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;16&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;32&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;64&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;1&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;128&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die Ergebnisregister ADCL und ADCH ==&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis des ADC ein 10 Bit Wert ist, passt dieser Wert naturgemäß nicht in ein einzelnes Register, das je bekanntlich nur 8 Bit breit ist. Daher wird das Ergebnis in 2 Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; abgelegt. Von den 10 Ergebnisbits sind die niederwertigsten 8 im Register &#039;&#039;&#039;ADCL&#039;&#039;&#039; abgelegt und die noch fehlenden 2 Bits werden im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; an den niederwertigsten Bitpositionen gespeichert.&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
                             9   8       7   6   5   4   3   2   1   0&lt;br /&gt;
&lt;br /&gt;
Diese Zuordnung kann aber auch geändert werden: Durch setzen des &#039;&#039;&#039;ADLAR&#039;&#039;&#039; Bits im &#039;&#039;&#039;ADMUX&#039;&#039;&#039; Register wird die Ausgabe geändert zu:&lt;br /&gt;
&lt;br /&gt;
              ADCH                                   ADCL&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
   +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
     9   8   7   6   5   4   3   2       1   0&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders dann interessant, wenn das ADC Ergebnis als 8 Bit Zahl weiterverarbeitet werden soll. In diesem Fall stehen die 8 höchstwertigen Bits bereits verarbeitungsfertig im Register &#039;&#039;&#039;ADCH&#039;&#039;&#039; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Beim Auslesen der ADC-Register ist zu beachten:&lt;br /&gt;
Immer zuerst &#039;&#039;&#039;ADCL&#039;&#039;&#039; und erst dann &#039;&#039;&#039;ADCH&#039;&#039;&#039; auslesen. Beim Zugriff auf &#039;&#039;&#039;ADCL&#039;&#039;&#039; wird das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register gegenüber Veränderungen vom &#039;&#039;&#039;ADC&#039;&#039;&#039; gesperrt. Erst beim nächsten Auslesen des &#039;&#039;&#039;ADCH&#039;&#039;&#039;-Registers wird diese Sperre wieder aufgehoben. Dadurch ist sichergestellt, daß die Inhalte von &#039;&#039;&#039;ADCL&#039;&#039;&#039; und &#039;&#039;&#039;ADCH&#039;&#039;&#039; immer aus demselben Wandlungsergebnis stammen, selbst wenn der &#039;&#039;&#039;ADC&#039;&#039;&#039; im Hintergrund selbsttätig weiterwandelt. Das &#039;&#039;&#039;ADCH&#039;&#039;&#039; Register &#039;&#039;&#039;muss&#039;&#039;&#039; ausgelesen werden!&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Das folgende Programm liest in einer Schleife ständig den ADC aus und verschickt des Ergebnis im Klartext über die [[AVR-Tutorial: UART|UART]]. Zur Verringerung des unvermeidlichen Rauschens werden 256 Messwerte herangezogen und deren Mittelwert als endgültiges Messergebnis gewertet. Dazu werden die einzelnen Messungen in den Registern temp2, temp3, temp4 als 24 Bit Zahl aufaddiert. Die Division durch 256 erfolgt dann ganz einfach dadurch, dass das Register temp2 verworfen wird und die Register temp3 und temp4 als 16 Bit Zahl aufgefasst werden. Eine Besonderheit ist noch, dass je nach dem Wert in temp2 die 16 Bit Zahl in temp3 und temp4 noch aufgerundet wird: Enthält temp2 einen Wert größer als 128, dann wird zur 16 Bit Zahl in temp3/temp4 noch 1 dazu addiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp1     = r16         ; allgemeines temp Register, zur krufristigen Verwendung&lt;br /&gt;
.def temp2     = r17         ; Register für 24 Bit Addition, Lowest Byte&lt;br /&gt;
.def temp3     = r18         ; Register für 24 Bit Addition, Middle Byte&lt;br /&gt;
.def temp4     = r19         ; Register für 24 Bit Addition, Highest Byte&lt;br /&gt;
.def adlow     = r20         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def adhigh    = r21         ; Ergebnis vom ADC / Mittelwert der 256 Messungen&lt;br /&gt;
.def messungen = r22         ; Schleifenzähler für die Messungen&lt;br /&gt;
.def ztausend  = r23         ; Zehntausenderstelle des ADC Wertes&lt;br /&gt;
.def tausend   = r24         ; Tausenderstelle des ADC Wertes&lt;br /&gt;
.def hundert   = r25         ; Hunderterstelle des ADC Wertes&lt;br /&gt;
.def zehner    = r26         ; Zehnerstelle des ADC Wertes&lt;br /&gt;
.def zeichen   = r27         ; Zeichen zur Ausgabe auf den UART&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programm los&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(RAMEND)                  ; Stackpointer initialisieren&lt;br /&gt;
    out     SPL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp1&lt;br /&gt;
 &lt;br /&gt;
;UART Initalisierung&lt;br /&gt;
 &lt;br /&gt;
    ldi     temp1, LOW(UBRR_VAL)                ; Baudrate einstellen&lt;br /&gt;
    out     UBRRL, temp1&lt;br /&gt;
    ldi     temp1, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp1&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, TXEN                         ; TX einschalten&lt;br /&gt;
 &lt;br /&gt;
; ADC initialisieren: Single Conversion, Vorteiler 128&lt;br /&gt;
&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;REFS0)                   ; Kanal 0, interne Referenzspannung 5V&lt;br /&gt;
    out     ADMUX, temp1&lt;br /&gt;
    ldi     temp1, (1&amp;lt;&amp;lt;ADEN) | (1&amp;lt;&amp;lt;ADPS2) | (1&amp;lt;&amp;lt;ADPS1) | (1&amp;lt;&amp;lt;ADPS0)&lt;br /&gt;
    out     ADCSRA, temp1&lt;br /&gt;
 &lt;br /&gt;
Main:&lt;br /&gt;
    clr     temp1&lt;br /&gt;
    clr     temp2&lt;br /&gt;
    clr     temp3&lt;br /&gt;
    clr     temp4&lt;br /&gt;
&lt;br /&gt;
    ldi     messungen, 0        ; 256 Schleifendurchläufe&lt;br /&gt;
 &lt;br /&gt;
; neuen ADC-Wert lesen  (Schleife - 256 mal)&lt;br /&gt;
&lt;br /&gt;
sample_adc:&lt;br /&gt;
    sbi     ADCSRA, ADSC        ; den ADC starten&lt;br /&gt;
 &lt;br /&gt;
wait_adc:&lt;br /&gt;
    sbic    ADCSRA, ADSC        ; wenn der ADC fertig ist, wird dieses Bit gelöscht&lt;br /&gt;
    rjmp    wait_adc&lt;br /&gt;
 &lt;br /&gt;
; ADC einlesen:&lt;br /&gt;
&lt;br /&gt;
    in      adlow, ADCL         ; immer zuerst LOW Byte lesen&lt;br /&gt;
    in      adhigh, ADCH        ; danach das mittlerweile gesperrte High Byte&lt;br /&gt;
 &lt;br /&gt;
; alle 256 ADC-Werte addieren&lt;br /&gt;
; dazu wird mit den Registern temp4, temp3 und temp2 ein&lt;br /&gt;
; 24-Bit breites Akkumulationsregister gebildet, in dem&lt;br /&gt;
; die 10 Bit Werte aus adhigh, adlow aufsummiert werden&lt;br /&gt;
&lt;br /&gt;
    add     temp2, adlow        ; addieren&lt;br /&gt;
    adc     temp3, adhigh       ; addieren über Carry&lt;br /&gt;
    adc     temp4, temp1        ; addieren über Carry, temp1 enthält 0&lt;br /&gt;
    dec     messungen           ; Schleifenzähler MINUS 1&lt;br /&gt;
    brne    sample_adc          ; wenn noch keine 256 ADC Werte -&amp;gt; nächsten Wert einlesen&lt;br /&gt;
 &lt;br /&gt;
; Aus den 256 Werten den Mittelwert berechnen&lt;br /&gt;
; Bei 256 Werten ist das ganz einfach: Das niederwertigste Byte&lt;br /&gt;
; (im Register temp2) fällt einfach weg&lt;br /&gt;
;&lt;br /&gt;
; allerdings wird der Wert noch gerundet&lt;br /&gt;
&lt;br /&gt;
    cpi     temp2,128           ; &amp;quot;Kommastelle&amp;quot; kleiner als 128 ?&lt;br /&gt;
    brlo    no_round            ; ist kleiner ==&amp;gt; Sprung&lt;br /&gt;
 &lt;br /&gt;
; Aufrunden&lt;br /&gt;
    subi    temp3, low(-1)      ; addieren von 1&lt;br /&gt;
    sbci    temp4, high(-1)     ; addieren des Carry&lt;br /&gt;
 &lt;br /&gt;
no_round:&lt;br /&gt;
&lt;br /&gt;
;   Ergebnis nach adlow und adhigh kopieren&lt;br /&gt;
;   damit die temp Register frei werden&lt;br /&gt;
&lt;br /&gt;
    mov     adlow, temp3&lt;br /&gt;
    mov     adhigh, temp4&lt;br /&gt;
 &lt;br /&gt;
;in ASCII umwandeln&lt;br /&gt;
&lt;br /&gt;
    ldi     ztausend, -1 + &#039;0&#039;&lt;br /&gt;
_a6ser:&lt;br /&gt;
    inc     ztausend&lt;br /&gt;
    subi    adlow, low(10000)   ; -10,000&lt;br /&gt;
    sbci    adhigh, high(10000)&lt;br /&gt;
    brcc    _a6ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     tausend, 10 + &#039;0&#039;&lt;br /&gt;
_a7ser:&lt;br /&gt;
    dec     tausend&lt;br /&gt;
    subi    adlow, low(-1000)   ; +1000&lt;br /&gt;
    sbci    adhigh, high(-1000)&lt;br /&gt;
    brcs    _a7ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     hundert, -1 + &#039;0&#039;&lt;br /&gt;
_a8ser:&lt;br /&gt;
    inc     hundert&lt;br /&gt;
    subi    adlow, low(100)    ; -100&lt;br /&gt;
    sbci    adhigh, high(100)&lt;br /&gt;
    brcc    _a8ser&lt;br /&gt;
 &lt;br /&gt;
    ldi     zehner, 10 + &#039;0&#039;&lt;br /&gt;
_a9ser:&lt;br /&gt;
    dec     zehner&lt;br /&gt;
    subi    adlow, -10          ; +10&lt;br /&gt;
    brcs    _a9ser&lt;br /&gt;
    subi    adlow,-&#039;0&#039;&lt;br /&gt;
 &lt;br /&gt;
;an UART Senden&lt;br /&gt;
&lt;br /&gt;
    mov     zeichen, ztausend   ; Zehntausender Stelle&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, tausend    ; Tausender Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, hundert    ; Hunderter Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, zehner     ; Zehner Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    mov     zeichen, adlow      ; Einer Stelle ausgeben&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 13         ; CR&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
    ldi     zeichen, 10         ; LF&lt;br /&gt;
    rcall   transmit&lt;br /&gt;
 &lt;br /&gt;
    rjmp    Main&lt;br /&gt;
 &lt;br /&gt;
transmit:&lt;br /&gt;
    sbis    UCSRA,UDRE          ; Warten, bis UDR bereit ist ...&lt;br /&gt;
    rjmp    transmit&lt;br /&gt;
    out     UDR, zeichen        ; und Zeichen ausgeben&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Uhr|&lt;br /&gt;
zurücklink=AVR-Tutorial: Uhr|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Tasten|&lt;br /&gt;
vorlink=AVR-Tutorial: Tasten}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_UART&amp;diff=25219</id>
		<title>AVR-Tutorial: UART</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_UART&amp;diff=25219"/>
		<updated>2007-12-25T21:19:06Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Fehler in Beispielen korrigiert, Beispiele verbessert, Formatierung vereinheitlicht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wie viele andere Controller besitzen die meisten AVRs einen &#039;&#039;&#039;[[UART]]&#039;&#039;&#039; (&#039;&#039;&#039;U&#039;&#039;&#039;niversal &#039;&#039;&#039;A&#039;&#039;&#039;synchronous &#039;&#039;&#039;R&#039;&#039;&#039;eceiver and &#039;&#039;&#039;T&#039;&#039;&#039;ransmitter). Das ist eine serielle Schnittstelle, die meistens zur Datenübertragung zwischen Mikrocontroller und PC genutzt wird. Zur Übertragung werden zwei Pins am Controller benötigt: &#039;&#039;&#039;TXD&#039;&#039;&#039; und &#039;&#039;&#039;RXD&#039;&#039;&#039;. Über &#039;&#039;&#039;TXD&#039;&#039;&#039; (&amp;quot;Transmit Data&amp;quot;) werden Daten gesendet, &#039;&#039;&#039;RXD&#039;&#039;&#039; (&amp;quot;Receive Data&amp;quot;) dient zum Empfang von Daten.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Um den UART des Mikrocontrollers zu verwenden, muss der Versuchsaufbau um folgende Bauteile erweitert werden: &lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR-RS232.png|640px]]&lt;br /&gt;
&lt;br /&gt;
Auf dem Board vom [http://shop.mikrocontroller.net/ Shop] sind diese Bauteile bereits enthalten, man muss nur noch die Verbindungen zwischen MAX232 und AVR herstellen wie im [http://shop.mikrocontroller.net/images/avrplat28-app.jpg Bild] zu sehen.&lt;br /&gt;
&lt;br /&gt;
* Der MAX232 ist ein [[Pegelwandler]], der die -12V/+12V Signale an der seriellen Schnittstelle des PCs zu den 5V/0V des AVRs kompatibel macht.&lt;br /&gt;
* C1 ist ein kleiner Keramikkondensator, wie er immer wieder zur Entkopplung der Versorgungsspannungen an digitalen ICs verwendet wird.&lt;br /&gt;
* Die vier Kondensatoren C2..C5 sind Elektrolytkondensatoren (Elkos). Auf die richtige Polung achten! Minus ist der Strich auf dem Gehäuse. Der exakte Wert ist hier relativ unkritisch, in der Praxis sollte alles von ca. 1µF bis 47µF mit einer Spannungsfestigkeit von 16V und höher funktionieren.&lt;br /&gt;
* X1 ist ein weiblicher 9-poliger SUB-D-Verbinder.&lt;br /&gt;
* Die Verbindung zwischen PC und Mikrocontroller erfolgt über ein 9-poliges Modem-Kabel (nicht [http://de.wikipedia.org/wiki/Nullmodem-Kabel Nullmodem]-Kabel!), das an den seriellen Port des PCs angeschlossen wird. Bei einem Modem-Kabel sind die Pins 2 und 3 des einen Kabelendes mit den Pins 2 und 3 des anderen Kabelendes durchverbunden. Bei einem Nullmodem Kabel sind die Leitungen gekreuzt, sodass Pin 2 von der einen Seite mit Pin 3 auf der anderen Seite verbunden ist und umgekehrt.&lt;br /&gt;
* Als Faustregel kann man annehmen: Befinden sich an den beiden Enden des Kabels die gleiche Art von Anschlüssen (Männchen = Stecker; Weibchen = Buchse), dann benötigt man ein gekreuztes Kabel, also ein Nullmodem-Kabel. Am PC-Anschluss selbst befindet sich ein Stecker, also ein Männchen, sodaß am Kabel auf dieser Seite eine Buchse (also ein Weibchen) sitzen muss. Da am AVR laut obigem Schaltbild eine Buchse (also ein Weibchen) verbaut wird, muss daher an diesem Ende des Kabels ein Stecker sitzen. Das Kabel hat daher an einem Ende einen Stecker und am anderen Ende eine Buchse und ist daher ein normales Modem-Kabel ( = nicht gekreuzt).&lt;br /&gt;
&lt;br /&gt;
[[Bild:USART_Kabel.gif]]&lt;br /&gt;
&lt;br /&gt;
== Software ==&lt;br /&gt;
&lt;br /&gt;
=== UART konfigurieren ===&lt;br /&gt;
&lt;br /&gt;
Als erstes muss die gewünschte Baudrate im Register &#039;&#039;&#039;UBRR&#039;&#039;&#039; festgelegt werden. Der in dieses Register zu schreibende Wert errechnet sich nach der folgenden Formel: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;UBRR = \frac {Taktfrequenz} { 16 \cdot Baudrate } - 1&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim AT90S4433 kann man den Wert direkt in das Register &#039;&#039;&#039;UBRR&#039;&#039;&#039; laden, beim ATmega8 gibt es für &#039;&#039;&#039;UBRR&#039;&#039;&#039; zwei Register: &#039;&#039;&#039;UBRRL&#039;&#039;&#039; (Low-Byte) und &#039;&#039;&#039;UBRRH&#039;&#039;&#039; (High-Byte). Im Normalfall steht in &#039;&#039;&#039;UBRRH&#039;&#039;&#039; 0, da der berechnete Wert kleiner als 256 ist und somit in &#039;&#039;&#039;UBRRL&#039;&#039;&#039; alleine passt. Beachtet werden muss, dass das Register &#039;&#039;&#039;UBRRH&#039;&#039;&#039; vor dem Register &#039;&#039;&#039;UBRRL&#039;&#039;&#039; beschrieben werden muss. Der Schreibzugriff auf &#039;&#039;&#039;UBRRL&#039;&#039;&#039; löst das Neusetzen des internen Taktteilers aus.&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#ff0000;&amp;quot;&amp;gt;&#039;&#039;&#039;WICHTIGER HINWEIS!&#039;&#039;&#039;&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf Grund permanent wiederkehrender Nachfrage sei hier &#039;&#039;&#039;AUSDRÜCKLICH&#039;&#039;&#039; darauf hingewiesen, dass bei Verwendung des UART unbedingt ein Quarz oder Ouarzoszillator verwendet werden muss! Der interne RC-Oszillator der AVRs ist zu ungenau! Damit kann es in Ausnahmefällen funktionieren, muss es aber nicht! Auch ist der interne Oszillator temperaturempfindlich. Damit hat man dann den schönen Effekt, dass eine UART-Schaltung die im Winter noch funktionierte, im Sommer den Dienst verweigert.&lt;br /&gt;
&lt;br /&gt;
Außerdem muss bei der Berechnung von &#039;&#039;&#039;UBRR&#039;&#039;&#039; geprüft werden, ob mit der verwendeten Quarzfrequenz die gewünschte Baudrate mit einem Fehler von &amp;lt;1% generiert werden kann. Das Datenblatt bietet hier sowohl die Formel als auch Tabellen unter der Überschrift des U(S)ART an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt; Fehler_{Baudrate}[%] = (\frac{UBRR_{gerundet}+1}{UBRR_{genau}+1} -1 ) \cdot 100&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe auch [[Baudratenquarz]]&lt;br /&gt;
&lt;br /&gt;
Wer es ganz einfach haben will, nimmt die folgenden Macros. Die rechnen sogar den Fehler aus und brechen die Assemblierung ggf. ab. Das ist dann praktisch idiotensicher.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
Um den Sendekanal des UART zu aktivieren, muss das Bit &#039;&#039;&#039;TXEN&#039;&#039;&#039; im UART Control Register &#039;&#039;&#039;UCSRB&#039;&#039;&#039; auf 1 gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Danach kann das zu sendende Byte in das Register &#039;&#039;&#039;UDR&#039;&#039;&#039; eingeschrieben werden - vorher muss jedoch sichergestellt werden, dass das Register leer ist, die vorhergehende Übertragung also schon abgeschlossen wurde. Dazu wird getestet, ob das Bit &#039;&#039;&#039;UDRE&#039;&#039;&#039; (&amp;quot;UART Data Register Empty&amp;quot;) im Register &#039;&#039;&#039;UCSRA&#039;&#039;&#039; auf 1 ist.&lt;br /&gt;
&lt;br /&gt;
Genaueres über die UART-Register findet man im Datenblatt des Controllers. &lt;br /&gt;
&lt;br /&gt;
Der ATmega8 bietet noch viele weitere Optionen zur Konfiguration des UARTs, aber für die Datenübertragung zum PC sind im Normalfall keine anderen Einstellungen notwendig. &lt;br /&gt;
&lt;br /&gt;
=== Senden von Zeichen ===&lt;br /&gt;
&lt;br /&gt;
Das Beispielprogramm überträgt die Zeichenkette &amp;quot;Test!&amp;quot; in einer Endlosschleife an den PC. Die folgenden Beispiele sind für den ATmega8 geschrieben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp    = r16                              ; Register für kleinere Arbeiten&lt;br /&gt;
.def zeichen = r17                              ; in diesem Register wird das Zeichen an die&lt;br /&gt;
                                                ; Ausgabefunktion übergeben&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))      ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB,TXEN                  ; TX aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    ldi     zeichen, &#039;T&#039;&lt;br /&gt;
    rcall   serout                      ; Unterprogramm aufrufen&lt;br /&gt;
    ldi     zeichen, &#039;e&#039;&lt;br /&gt;
    rcall   serout                      ; Unterprogramm aufrufen&lt;br /&gt;
    ldi     zeichen, &#039;s&#039;&lt;br /&gt;
    rcall   serout                      ; ...&lt;br /&gt;
    ldi     zeichen, &#039;t&#039;&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, &#039;!&#039;&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, 10&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    ldi     zeichen, 13&lt;br /&gt;
    rcall   serout&lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
serout:&lt;br /&gt;
    sbis    UCSRA,UDRE                  ; Warten bis UDR für das nächste&lt;br /&gt;
                                        ; Byte bereit ist&lt;br /&gt;
    rjmp    serout&lt;br /&gt;
    out     UDR, zeichen&lt;br /&gt;
    ret                                 ; zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;&#039;rcall serout&#039;&#039;&#039; ruft ein kleines Unterprogramm auf, das zuerst wartet bis das Datenregister &#039;&#039;&#039;UDR&#039;&#039;&#039; von der vorhergehenden Übertragung frei ist, und anschließend das in zeichen (=r17) gespeicherte Byte an &#039;&#039;&#039;UDR&#039;&#039;&#039; ausgibt. &lt;br /&gt;
&lt;br /&gt;
Bevor &amp;lt;i&amp;gt;serout&amp;lt;/i&amp;gt; aufgerufen wird, wird zeichen jedesmal mit dem ASCII-Code des zu übertragenden Zeichens geladen (so wie in Teil 4 bei der LCD-Ansteuerung). Der Assembler wandelt Zeichen in einfachen Anführungsstrichen automatisch in den entsprechenden ASCII-Wert um. Nach dem Wort &amp;quot;Test!&amp;quot; werden noch die Codes 10 (New Line) und 13 (Carriage Return) gesendet, um dem Terminalprogramm mitzuteilen, dass eine neue Zeile beginnt.&lt;br /&gt;
&lt;br /&gt;
Eine Übersicht aller ASCII-Codes gibt es auf [http://www.asciitable.com/ www.asciitable.com].&lt;br /&gt;
&lt;br /&gt;
Die Berechnung der Baudrate wird übrigens nicht im Controller während der Programmausführung durchgeführt, sondern schon beim Assemblieren, wie man beim Betrachten der Listingdatei feststellen kann. &lt;br /&gt;
&lt;br /&gt;
Zum Empfang muss auf dem PC ein Terminal-Programm wie z.B. HyperTerminal gestartet werden. Der folgende Screenshot zeigt, welche Einstellungen im Programm vorgenommen werden müssen: &lt;br /&gt;
&lt;br /&gt;
http://www.mikrocontroller.net/images/hyperterminal.gif&lt;br /&gt;
&lt;br /&gt;
Linux-Benutzer können das entsprechende Device (z.B. /dev/ttyS0) mit stty konfigurieren und mit cat die empfangenen Daten anzeigen oder ein Terminalprogramm wie minicom nutzen.&lt;br /&gt;
&lt;br /&gt;
=== Senden von Zeichenketten ===&lt;br /&gt;
&lt;br /&gt;
Eine bequemere Methode um längere Zeichenketten (Strings) zu übertragen ist hier zu sehen. Dabei werden die Zeichenketten im Flash gespeichert. Als Abschluss des Strings wird der Wert 0x00 genutzt, so wie auch in der Programmiersprache C. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp    = r16                              ; Register für kleinere Arbeiten&lt;br /&gt;
.def zeichen = r17                              ; in diesem Register wird das Zeichen an die&lt;br /&gt;
                                                ; Ausgabefunktion übergeben&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
; hier geht unser Programm los&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB,TXEN                      ; TX aktivieren&lt;br /&gt;
&lt;br /&gt;
loop:&lt;br /&gt;
    ldi     zl,low(my_string*2);            ; Z Pointer laden&lt;br /&gt;
    ldi     zh,high(my_string*2);&lt;br /&gt;
    rcall   serout_string&lt;br /&gt;
    rjmp    loop&lt;br /&gt;
&lt;br /&gt;
; Ausgabe eines Strings aus dem Flash&lt;br /&gt;
&lt;br /&gt;
serout_string:&lt;br /&gt;
    lpm                             ; nächstes Byte aus dem Flash laden&lt;br /&gt;
    and     r0,r0                   ; = Null? &lt;br /&gt;
    breq    serout_string_ende      ; wenn ja, -&amp;gt; Ende&lt;br /&gt;
serout_string_wait:&lt;br /&gt;
    sbis    UCSRA,UDRE              ; Warten bis UDR für das nächste&lt;br /&gt;
                                    ; Byte bereit ist&lt;br /&gt;
    rjmp    serout_string_wait&lt;br /&gt;
    out     UDR, r0&lt;br /&gt;
    adiw    zl:zh,1                 ; Zeiger erhöhen&lt;br /&gt;
    rjmp    serout_string           ; nächstes Zeichen bearbeiten&lt;br /&gt;
serout_string_ende:&lt;br /&gt;
    ret                             ; zurück zum Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
; Hier wird jetzt der String definiert und im Flash gespeichert&lt;br /&gt;
&lt;br /&gt;
my_string:  .db &amp;quot;Test!&amp;quot;,10,13,0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfangen von Zeichen per Polling===&lt;br /&gt;
&lt;br /&gt;
Der AVR kann nicht nur Daten seriell senden, sondern auch empfangen. Dazu muss man, nachdem die Baudrate wie oben beschrieben eingestellt wurde, das Bit &#039;&#039;&#039;RXEN&#039;&#039;&#039; setzen. &lt;br /&gt;
&lt;br /&gt;
Sobald der UART ein Byte über die serielle Verbindung empfangen hat, wird das Bit &#039;&#039;&#039;RXC&#039;&#039;&#039; im Register &#039;&#039;&#039;UCSRA&#039;&#039;&#039; gesetzt, um anzuzeigen, dass ein Byte im Register &#039;&#039;&#039;UDR&#039;&#039;&#039; zur Weiterverarbeitung bereitsteht. Sobald es aus &#039;&#039;&#039;UDR&#039;&#039;&#039; gelesen wurde, wird &#039;&#039;&#039;RXC&#039;&#039;&#039; automatisch wieder gelöscht, bis das nächste Byte angekommen ist. &lt;br /&gt;
&lt;br /&gt;
Das erste einfache Testprogramm soll das empfangene Byte auf den an Port D angeschlossenen LEDs ausgeben. Dabei sollte man daran denken, dass PD0 (RXD) bereits für die Datenübertragung zuständig ist, so dass das entsprechende Bit im Register PORTD keine Funktion hat und damit auch nicht für die Datenanzeige verwendet werden kann. &lt;br /&gt;
&lt;br /&gt;
Nachdem der UART konfiguriert ist, wartet das Programm einfach in der Hauptschleife darauf, dass ein Byte über den UART ankommt (z.B. indem man im Terminalprogramm ein Zeichen eingibt), also &#039;&#039;&#039;RXC&#039;&#039;&#039; gesetzt wird. Sobald das passiert, wird das Register &#039;&#039;&#039;UDR&#039;&#039;&#039;, in dem die empfangenen Daten stehen, nach &amp;lt;i&amp;gt;temp&amp;lt;/i&amp;gt; eingelesen und an den Port D ausgegeben. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port D = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRD, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren&lt;br /&gt;
&lt;br /&gt;
receive_loop:&lt;br /&gt;
   sbis     UCSRA, RXC                      ; warten bis ein Byte angekommen ist&lt;br /&gt;
   rjmp     receive_loop&lt;br /&gt;
   in       temp, UDR                       ; empfangenes Byte nach temp kopieren&lt;br /&gt;
   out      PORTD, temp                     ; und an Port D ausgeben.&lt;br /&gt;
   rjmp     receive_loop                    ; zurück zum Hauptprogramm&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfangen von Zeichen per Interrupt ===&lt;br /&gt;
&lt;br /&gt;
Dieses Programm lässt sich allerdings noch verfeinern. Statt in der Hauptschleife auf die Daten zu warten, kann man auch veranlassen dass ein Interrupt ausgelöst wird, sobald ein Byte angekommen ist. Das sieht in der einfachsten Form so aus: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
.org 0x00&lt;br /&gt;
        rjmp main&lt;br /&gt;
&lt;br /&gt;
.org URXCaddr                                   ; Interruptvektor für UART-Empfang&lt;br /&gt;
        rjmp int_rxc&lt;br /&gt;
&lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
&lt;br /&gt;
main:&lt;br /&gt;
&lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port D = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRD, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXCIE                    ; Interrupt bei Empfang&lt;br /&gt;
    sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren&lt;br /&gt;
    &lt;br /&gt;
    sei                                     ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
loop:&lt;br /&gt;
    rjmp loop                               ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
    push    temp                            ; temp auf dem Stack sichern&lt;br /&gt;
    in      temp, UDR                       ; epfangendes Byte lesen,&lt;br /&gt;
                                            ; dadurch wird auch der Interrupt gelöscht&lt;br /&gt;
    out     PORTD, temp                     ; Daten ausgeben&lt;br /&gt;
    pop     temp                            ; temp wiederherstellen&lt;br /&gt;
    reti                                    ; Interrupt beenden&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode hat den großen Vorteil, dass das Hauptprogramm (hier nur eine leere Endlosschleife) andere Dinge erledigen kann, während der Controller Daten empfängt. Auf diese Weise kann man mehrere Aktionen quasi gleichzeitig ausführen, da das Hauptprogramm nur kurz unterbrochen wird, um die empfangenen Daten zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Probleme können allerdings auftreten, wenn in der Interruptroutine die gleichen Register verwendet werden wie im Hauptprogramm, da dieses ja an beliebigen Stellen durch den Interrupt unterbrochen werden kann. Damit sich aus der Sicht der Hauptschleife durch den Interruptaufruf nichts ändert, müssen alle in der Interruptroutine geänderten Register am Anfang der Routine gesichert und am Ende wiederhergestellt werden. Das gilt vor allem für das CPU-Statusregister (&#039;&#039;&#039;SREG&#039;&#039;&#039;)! Sobald ein einziger Befehl im Interrupt ein einziges Bit im SREG beeinflusst, muss das SREG gesichert werden. Das ist praktisch fast immer der Fall, nur in dem ganz einfachen Beispiel oben ist es überflüssig, weil die verwendeten Befehle das SREG nicht beeinflussen. In diesem Zusammenhang wird der [[Stack]] wieder interessant. Um die Register zu sichern, kann man sie mit &#039;&#039;&#039;push&#039;&#039;&#039; oben auf den Stapel legen und am Ende wieder in der umgekehrten Reihenfolge(!) mit &#039;&#039;&#039;pop&#039;&#039;&#039; vom Stapel herunternehmen.&lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispielprogramm werden die empfangenen Daten nun nicht mehr komplett angezeigt. Stattdessen kann man durch Eingabe einer 1 oder einer 0 im Terminalprogramm eine LED (an PB0) an- oder ausschalten. Dazu wird das empfangene Byte in der Interruptroutine mit den entsprechenden ASCII-Codes der Zeichen 1 und 0 (siehe [http://www.asciitable.com/ www.asciitable.com]) verglichen.&lt;br /&gt;
&lt;br /&gt;
Für den [[AVR-Tutorial:_Vergleiche|Vergleich]] eines Registers mit einer Konstanten gibt es den Befehl &#039;&#039;&#039;cpi register, konstante&#039;&#039;&#039;. Das Ergebnis dieses Vergleichs kann man mit den Befehlen &#039;&#039;&#039;breq label&#039;&#039;&#039; (springe zu label, wenn gleich) und &#039;&#039;&#039;brne label&#039;&#039;&#039; (springe zu label, wenn ungleich) auswerten. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
.equ F_CPU = 4000000                            ; Systemtakt in Hz&lt;br /&gt;
.equ BAUD  = 9600                               ; Baudrate&lt;br /&gt;
&lt;br /&gt;
; Berechnungen&lt;br /&gt;
.equ UBRR_VAL   = ((F_CPU+BAUD*8)/(BAUD*16)-1)  ; clever runden&lt;br /&gt;
.equ BAUD_REAL  = (F_CPU/(16*(UBRR_VAL+1)))     ; Reale Baudrate&lt;br /&gt;
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000)  ; Fehler in Promille&lt;br /&gt;
&lt;br /&gt;
.if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))       ; max. +/-10 Promille Fehler&lt;br /&gt;
  .error &amp;quot;Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!&amp;quot;&lt;br /&gt;
.endif&lt;br /&gt;
&lt;br /&gt;
.org 0x00&lt;br /&gt;
        rjmp main&lt;br /&gt;
&lt;br /&gt;
.org URXCaddr&lt;br /&gt;
        rjmp int_rxc&lt;br /&gt;
&lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
main:&lt;br /&gt;
    &lt;br /&gt;
    ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, LOW(RAMEND)&lt;br /&gt;
    out     SPL, temp&lt;br /&gt;
    ldi     temp, HIGH(RAMEND)&lt;br /&gt;
    out     SPH, temp&lt;br /&gt;
&lt;br /&gt;
    ; Port B = Ausgang&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, 0xFF&lt;br /&gt;
    out     DDRB, temp&lt;br /&gt;
&lt;br /&gt;
    ; Baudrate einstellen&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, HIGH(UBRR_VAL)&lt;br /&gt;
    out     UBRRH, temp&lt;br /&gt;
    ldi     temp, LOW(UBRR_VAL)&lt;br /&gt;
    out     UBRRL, temp&lt;br /&gt;
&lt;br /&gt;
    ; Frame-Format: 8 Bit&lt;br /&gt;
&lt;br /&gt;
    ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)&lt;br /&gt;
    out     UCSRC, temp&lt;br /&gt;
&lt;br /&gt;
    sbi     UCSRB, RXCIE                ; Interrupt bei Empfang&lt;br /&gt;
    sbi     UCSRB, RXEN                 ; RX (Empfang) aktivieren&lt;br /&gt;
    &lt;br /&gt;
    sei                                 ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
loop:&lt;br /&gt;
    rjmp loop                           ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
    push    temp                        ; temp auf dem Stack sichern&lt;br /&gt;
    in      temp, sreg                  ; SREG sichern&lt;br /&gt;
    push    temp&lt;br /&gt;
    &lt;br /&gt;
    in      temp, UDR                   ; UART Daten lesen&lt;br /&gt;
    cpi     temp, &#039;1&#039;                   ; empfangenes Byte mit &#039;1&#039; vergleichen&lt;br /&gt;
    brne    int_rxc_1                   ; wenn nicht gleich, dann zu int_rxc_1&lt;br /&gt;
    cbi     PORTB, 0                    ; LED einschalten, low aktiv&lt;br /&gt;
    rjmp    int_rxc_2                   ; Zu int_rxc_2 springen&lt;br /&gt;
int_rxc_1:&lt;br /&gt;
    cpi     temp, &#039;0&#039;                   ; empfangenes Byte mit &#039;0&#039; vergleichen&lt;br /&gt;
    brne    int_rxc_2                   ; wenn nicht gleich, dann zu int_rxc_2&lt;br /&gt;
    sbi     PORTB, 0                    ; LED ausschalten, low aktiv&lt;br /&gt;
int_rxc_2:&lt;br /&gt;
&lt;br /&gt;
    pop     temp&lt;br /&gt;
    out     sreg, temp                  ; SREG wiederherstellen&lt;br /&gt;
    pop     temp                        ; temp wiederherstellen&lt;br /&gt;
    reti&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Mehrfachverzweigung|&lt;br /&gt;
zurücklink=AVR-Tutorial: Mehrfachverzweigung|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Speicher|&lt;br /&gt;
vorlink=AVR-Tutorial: Speicher}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25217</id>
		<title>Interrupt</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25217"/>
		<updated>2007-12-25T19:14:36Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Abschnitt eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein &#039;&#039;&#039;Interrupt&#039;&#039;&#039; ausgelöst. Dabei wird das Programm unterbrochen und ein Unterprogramm, die sogenannte &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine (&#039;&#039;&#039;ISR&#039;&#039;&#039;), aufgerufen. Wenn dieses beendet ist läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
Bei [[Mikrocontroller]]n werden Interrupts z.B. ausgelöst wenn:&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Pegel ändert&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine [[Seriell|serielle]] Übertragung abgeschlossen ist&lt;br /&gt;
* eine Messung des [[ADC |Analog-Digital-Wandlers]] abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
== Wichtige Eigenschaften von ISRs ==&lt;br /&gt;
&lt;br /&gt;
ISRs reagieren auf ein bestimmtes Ereignis, welches relativ oft oder selten passiert. Prinzipiell sollte man ISRs möglichst kurz halten und schnell beenden. &#039;&#039;&#039;Im Mittel muss die Interruptroutine kürzer sein als die Periodendauer des Ereignises, anderenfalls wird es passieren, dass Interrupts &amp;quot;verschluckt&amp;quot; werden&#039;&#039;&#039;, d.h. beim UART gehen Daten verloren, beim Timer gehen Zählzyklen verloren, beim AD-Wandler gehen Daten verloren etc. &#039;&#039;&#039;Solche verschluckten Interrupts sind bisweilen schwer zu finden, weil es bisweilen nur sehr wenige in ganz bestimmten Konstellationen sind.&#039;&#039;&#039; Wenn dann eine per Timer realisierte Uhr in der Stunde um 1s falsch geht, merkt man das oft nicht. &#039;&#039;&#039;Langwierige Berechnungen, Auswertungen, Ausgaben oder gar Warteschleifen haben in ISRs nichts zu suchen.&#039;&#039;&#039; Typische C-Funktionen wie printf(), scanf(), längere Ausgaben auf ein [[LCD]] etc. sollte man nicht in ISRs vornehmen. Hier kommt sinnvollerweise eine andere Programmiertechnik zu Einsatz, nämlich die Übergabe von Parametern bzw. Steuersignalen an das Hauptprogramm.&lt;br /&gt;
&lt;br /&gt;
=== Interruptsteuerung ===&lt;br /&gt;
&lt;br /&gt;
Interrupts müssen wie alle anderen Module und Funktionen eines Mikrocontrollers gesteuert werden. Dazu wird auf praktisch allen Mikrocontrollern ein zweistufiges System verwendet.&lt;br /&gt;
* Globale Interruptsteuerung über ein CPU-Statusbit: Beim [[AVR]] ist das das I-Bit (Interrupt) im Statusregister (SREG). Dieses Bit wirkt wie ein Hauptschalter und kann global die Ausführung aller Interrupts ein - und ausschalten. Das heisst aber nicht, dass während der Zeit der inaktiven Interrupts diese verloren gehen. Vielmehr wird das jeweilige Interruptbit gesetzt, und wenn die Interrupts wieder freigegeben werden wird der Interrupt ausgeführt. Verloren gehen Interrupts erst dann, wenn die Sperrzeit zu gross ist und währenddessen mehr als ein Interrupt vom selben Typ eintrifft. Siehe [[# Zeitverhalten eines Timerinterrupts]] und [[#Zeitverhalten des UART Empfangsinterrupt]]&lt;br /&gt;
* Lokale Interruptsteuerung für jede einzelne Interruptquelle über Maskenbits in mehreren Interruptmaskenregistern. Hier kann jede einzelne Interruptquelle individuell ein- und ausgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Dieses System hat eine Reihe von Vorteilen. So können sehr schnell und einfach alle Interrupts kurzzeitig gesperrt werden, wenn beispielsweise [[#Atomarer Datenzugriff | atomare]] Operationen durchgeführt werden sollen, oder besonders zeitkritische Abläufe ausgeführt werden. Danach können alle konfigurierten Interrupts einfach wieder freigeschaltet werden, ohne dass die CPU viele verschiedene Interruptmaskenbits verwalten müsste.&lt;br /&gt;
&lt;br /&gt;
Eine ISR wird demnach nur dann ausgeführt, wenn&lt;br /&gt;
* die Interrupts global freigeschaltet sind&lt;br /&gt;
* das individuelle Maskenbit gesetzt ist&lt;br /&gt;
* der Interrupt eintritt&lt;br /&gt;
&lt;br /&gt;
=== Verschachtelte Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Einige Mikrocontroller, wie z.B. der [[AVR]] kennen nur zwei CPU-Zustände. Normale Programmausführung und Interruptausführung, gesteuert durch das I-Bit der CPU. Die normale Programmausführung kann jederzeit durch Interrupts unterbrochen werden. Die Interruptausführung kann nicht durch neue Interrupts unterbrochen werden. Die ISR wird erst zu Ende bearbeitet, zurück in die normale Programmausführung gesprungen und erst dann werden neue, wartende (engl. pending) Interrupts bearbeitet.&lt;br /&gt;
&lt;br /&gt;
Etwas komplexere Mikrocontroller oder grosse Prozessoren bieten verschiedene Interruptlevel (Stufen) an . Dabei gilt meist je niedriger die Zahl des Levels, um so höher die Priorität. Ein Interrupt mit höherer Priorität kann einen Interrupt mit niedriger Priorität unterbrechen. Ein Interrupt mit gleicher Priorität wie der gerade bearbeitete Interrupt kann das im allgemeinen nicht. Das nennt man verschachtelte Interrupts (engl. nested interrupts). Klassische Vertreter hierfür sind [[8051]], PowerPC, [[X86]] und Motorola 68000.&lt;br /&gt;
&lt;br /&gt;
Auf dem AVR kann man [[AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen | verschachtelte Interrupts]] sowohl in Assembler als auch in C nachbilden, allerdings mit einigen Einschränkungen und Tücken. Das ist jedoch Leuten vorbehalten, die schon viel Erfahrung auf diesem Gebiet haben. Zu 99,9% braucht man sie nicht.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten eines Timerinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein Timerinterrupt wird im allgemeinen dazu genutzt, in konstanten, periodischen Abständen bestimmte Funktionen aufzurufen. Es ist möglich, dass während eines Timerinterrupts derselbe Interrupt wieder aktiv wird, weil die Routine sehr verzweigt ist und dieses Mal sehr lange dauert.&lt;br /&gt;
&lt;br /&gt;
Wenn zum Beispiel der Timerinterrupt mit einer Periodendauer von 100ms aufgerufen wird, er aber unter bestimmten Umständen 180ms benötigt, dann wird nach 100ms nach Eintritt in die ISR der Interrupt wieder aktiv, das Timer Interrupt Flag wird gesetzt. Da aber gerade ein Interrupt bearbeitet wird, wird er nicht sofort angesprungen, weil währenddessen die Interruptfunktion global gesperrt ist (beim AVR ist das I-Bit in der CPU gelöscht). Der Interrupt wird zu Ende bearbeitet, die CPU springt zurück zum Hauptprogramm. Dabei werden die Interrupts wieder global eingeschaltet. Der zwischenzeitlich eingetroffene und zwischengespeicherte Interrupt wird nun sofort ausgeführt, sodass das Hauptprogramm praktisch gar nicht weiter kommt, bestenfalls einen Maschinenbefehl. Nun sind aber nur noch 20ms bis zum nächsten Timerinterrupt übrig. Wenn dieser nun wieder 180 ms benötigt werden in dieser Zeit aber &#039;&#039;&#039;zwei&#039;&#039;&#039; Interrupts ausgelöst, nach 20ms und 120ms. Da diese aber nicht gezählt oder andersweitig einzeln gespeichert werden können, geht ein Interrupt verloren. Das ist ein Programmfehler.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten des UART Empfangsinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein [[UART]] Interrupt zum Empfang von Daten per [[RS232]] mit 115200 [[Baud]] ist ein recht häufiges Ereignis (1 Zeichen = 10 Bits = 86,8&amp;amp;mu;s). Wenn kontinuierlich Daten empfangen werden, wird nach jeweils 86,8&amp;amp;mu;s ein neuer Interrupt ausgelöst. Dabei wird das empfangene Datenbyte vom UART aus dem Empfangsschiebegregister in einem Puffer kopiert. Während das neue Zeichen Bit für Bit empfangen wird, wird es zunächst im Schieberegister des UART gespeichert. Die Daten im Puffer bleiben davon unberührt. Die CPU muss nun schnell das empfangene Datenbyte aus dem Empfangsbuffer auslesen. Die maximale Verzögerung, die sich die CPU erlauben kann von der Aktivierung des Interrupts bis zum tatsächlichen Auslesen des Datenregisters beträgt ziemlich genau die Übertragungszeit von einem Zeichen. Wenn bis dahin nicht das Zeichen von der CPU ausgelesen wurde, wird es vom UART überschrieben und ein Fehler im Statusregister des UART signalisiert (Overrun, Überlauf des Datenpuffers). Die UARTs in heutigen Mikrocontrollern haben mindestens ein Byte Puffer wie hier beschrieben. Die neueren [[AVR]]s haben sogar effektiv 3 Byte Puffer im UART, praktisch ein kleines [[FIFO]], womit der Datenempfang besser gepuffert werden kann, wenn die CPU gerade mit anderen sehr wichtigen Dingen beschäftigt ist. D.h. kurzzeitig kann sich die CPU erlauben, die Übertragungszeit von bis zu drei Zeichen zu warten, ehe sie die Daten ausliest. Dann müssen sie aber sehr schnell hintereinander gelesen werden. Im Mittel hat die CPU aber nur die Übertragungszeit eines Zeichens zur Verfügung, um es abzuholen.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenfassung ===&lt;br /&gt;
&lt;br /&gt;
Interruptserviceroutinen:&lt;br /&gt;
* sollten so kurz wie möglich gehalten werden&lt;br /&gt;
* können im Einzelfall nahezu doppelt so lange dauern wie die kürzeste Periodendauer des Ereignisses, ohne dass Interrupts verloren gehen (z.B. Timerinterrupt).&lt;br /&gt;
* dürfen im Mittel maximal solange dauern wie die kürzeste Periodendauer des Ereignisses&lt;br /&gt;
* dürfen maximal solange dauern, wie die kürzeste Periodendauer des Ereignisses, wenn man auf Nummer sicher gehen will, dass keine Interrupts verschluckt werden&lt;br /&gt;
* Die Interruptzeit versteht sich immer abzüglich einer kleinen Reserve für das Anspringen und Verlassen des Interrupt minus Panikreserve&lt;br /&gt;
&lt;br /&gt;
== Beispiele für die praktische Programmierung ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Als Mikrocontroller wird ein AVR vom Typ ATmega32 verwendet. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== Steuersignale zwischen ISR und Hauptprogramm ===&lt;br /&gt;
&lt;br /&gt;
In vielen Anwendungen wird ein Timer verwendet, um in regelmässigen Abständen bestimmte Aktionen auszuführen, wie z.B. Tasten abfragen, ADC-auslesen, ein LCD auffrischen etc. Wenn viele Dinge zu erledigen sind, nebenbei aber noch andere Interrupts verwendet werden, dann ist es notwendig die Funktionsaufrufe aus dem Timerinterrupt in die Hauptschleife zu verlagern. Der Interrupt signalisiert über eine Steuervariable (engl. Flag, Flagge), das ein neuer Timerzyklus begonnen hat. Dadurch wird der Timerinterrupt sehr kurz und die langwierigen, aber meist nicht zeitkritischen Funktionen werden als normales Programm ausgeführt. Damit kann die CPU auf andere Interrupts schnell reagieren.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auf jeden Fall, dass die Steuervariable, welche in der ISR und in der Hauptschleife verwendet wird, mit [http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword &#039;&#039;&#039;volatile&#039;&#039;&#039;] deklariert wird. Ausserdem muss sowohl der Lese- als auch Schreibzugriff auf die Steuersignale [[#Atomarer Datenzugriff | &#039;&#039;&#039;atomar&#039;&#039;&#039;]] sein. Auf dem AVR ist das mit 8 Bit Variablen direkt möglich, für grössere Variablen müssen die Interrupts kurzzeitig gesperrt werden.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel ist sehr einfach gehalten um das Prinzip zu veranschaulichen. Ein Timer mit einer Überlaufperiodendauer von ca. 65ms stösst periodisch eine Funktion zum Togglen einer LED an, welche dadurch mit ca. 7 Hz blinkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Timer Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
 &lt;br /&gt;
int main() {&lt;br /&gt;
 &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    TCCR2  = 6;                     // Vorteiler 256 -&amp;gt; ~65ms Überlaufperiode&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer Overflow Interrupt freischalten &lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
        if (flag == 1) { // Neuer Timerzyklus ?&lt;br /&gt;
            flag = 0;&lt;br /&gt;
 &lt;br /&gt;
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) &lt;br /&gt;
            PORTD ^= (1 &amp;lt;&amp;lt; PD5);    // LED toggeln&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER2_OVF_vect ) {&lt;br /&gt;
    flag = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UART mit Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Der UART ist ein oft benutztes Modul eines Mikrocontrollers. Anfänger nutzen ihn meist im sogenannten Polling Betrieb (engl. to poll, abfragen). D.h. wenn ein Zeichen empfangen werden soll, fragt eine Funktion den UART in einer Schleife ununterbrochen ab, ob Daten empfangen wurden. In dieser Zeit macht die CPU nichts anderes! Und wenn lange kein Zeichen eintrifft tut sie sehr lange nichts, sie ist praktisch blockiert! Senden verläuft ähnlich, nur dass hier die CPU vor dem Senden prüft, ob der UART ein neues Byte aufnehmen kann. D.h. während der UART selbsttätig das Zeichen sendet ist die CPU zum Warten verdammt. All diese Nachteile haben nur einen Vorteil. Die Funktionen und Mechanismen zur UART-Nutzung sind sehr einfach, klein und leicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
Will man aber die CPU nicht sinnlos warten lassen, was vor allem bei niedrigeren Baudraten ziemlich lange sein kann, muss man die Interrupts nutzen. Der AVR hat gleich drei davon.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RXC&#039;&#039;&#039; (Receive Complete): Ein Zeichen wurde empfangen.&lt;br /&gt;
* &#039;&#039;&#039;UDRE&#039;&#039;&#039; (UART Data Register Empty): Der Zwischenpuffer des Senders ist leer und kann ein neues Zeichen aufnehmen. Dieser Zwischenpuffer ist wichtig, um lückenlos auch bei hohen Baudraten senden zu können.&lt;br /&gt;
* &#039;&#039;&#039;TXC&#039;&#039;&#039; (Transmit Complete): Das aktuelle Zeichen wurde vollständig inclusive Stopbit gesendet und es liegt kein neues Datenbyte im Sendepuffer. Dieser Interrupt ist extrem nützlich für eine Halbduplexkommunikation, z.B. auf einem RS485-[[Bus]]. Hier kann man nach dem vollständigen Senden aller Bytes den Bustranceiver (z.B. MAX485) von Senden auf Empfangen umschalten, um den Bus freizugeben.&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung der Interrupts kann die CPU andere Dinge bearbeiten und muss nur kurz einen Interrupt ausführen, wenn ein Zeichen empfangen oder gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Kommunikation zwischen ISRs und Hauptschleife erfolgt wieder durch Flags und zwei Pufferarrays (uart_rx_buffer und uart_tx_buffer). Es gibt zwei Funktionen, eine zum Senden von Strings, eine zum Empfangen. Das Senden sowie Empfangen kann parallel erfolgen und läuft vollkommen unabhängig vom Hauptprogramm. Die Daten werden in spezielle Puffer kopiert, sodass das Hauptprogramm mit seinen Strings sofort weiterarbeiten kann. Im Beispiel ist die CPU nicht wirklich mit sinnvollen Dingen beschäftigt, zur Demonstration des Prinzips aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Um das das Programm real zu nutzen braucht man ein Terminalprogramm, z.B. Hyperterminal von Windows. Dort muss nur die richtige Baudrate eingestellt werden (9600 8N1, 9600 Baud, 8 Bits, keine Parität, 1 Stopbit, keine Flusskontrolle). Ausserdem muss man im Menu Datei -&amp;gt; Eigenschaften -&amp;gt; Einstellungen -&amp;gt; ASCII Konfiguration den Punkt &amp;quot;Eingegebene Zeichen lokal ausgeben (lokales Echo)&amp;quot; aktivieren. Nun kann man beliebige Texte eintippen. Mit RETURN wird die Eingabe abgeschlossen und der AVR vermittelt den empfangenen String an das Hauptprogramm. Diese sendet ihn einfach zurück, paralle dazu wird der String gemorst per LED angezeigt. Sollte es Probleme bei der Inbetriebnahme des UART geben, so findet man hier wichtige Hinweise zur [[AVR_Checkliste#UART.2FUSART | Fehlersuche]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* UART Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0/PD1 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen/senden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
// Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define F_CPU 3686400L               &lt;br /&gt;
// &amp;quot;Morsedauer&amp;quot; für ein Bit in Millisekunden&lt;br /&gt;
#define BITZEIT 100     &lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen für den UART&lt;br /&gt;
 &lt;br /&gt;
// Puffergrösse in Bytes, RX und TX sind gleich gross&lt;br /&gt;
#define uart_buffer_size 32             &lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t uart_rx_flag=0;            // Flag, String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_tx_flag=1;            // Flag, String komplett gesendet&lt;br /&gt;
char uart_rx_buffer[uart_buffer_size];      // Empfangspuffer&lt;br /&gt;
char uart_tx_buffer[uart_buffer_size];      // Sendepuffer&lt;br /&gt;
 &lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen String senden&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_t_flag==1 ist&lt;br /&gt;
// nur dann kann ein neuer String gesendet werden&lt;br /&gt;
 &lt;br /&gt;
void put_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_tx_flag==1) {&lt;br /&gt;
      // String daten ind en Sendepuffer kopieren&lt;br /&gt;
      strcpy(uart_tx_buffer, daten);      &lt;br /&gt;
      // Flag für &#039;Senden ist komplett&#039; löschen, &lt;br /&gt;
      uart_tx_flag = 0;                    &lt;br /&gt;
      // UDRE Interrupt einschalten, los gehts&lt;br /&gt;
      UCSRB |= (1&amp;lt;&amp;lt;UDRIE); &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen empfangenen String kopieren&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_rx_flag==1 ist&lt;br /&gt;
// anderenfalls ist der RX Buffer noch ungültig&lt;br /&gt;
 &lt;br /&gt;
void get_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_rx_flag==1) {&lt;br /&gt;
      // String kopieren&lt;br /&gt;
      strcpy(daten, uart_rx_buffer);      &lt;br /&gt;
      // Flag löschen&lt;br /&gt;
      uart_rx_flag = 0;                    &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Ein Byte im RS232 Format auf eine LED ausgeben&lt;br /&gt;
 &lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startbit, immer 0&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);           // LED aus&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
 &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)            // Prüfe Bit #0&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        else&lt;br /&gt;
            PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus      &lt;br /&gt;
        long_delay(BITZEIT);        &lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;                 // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
    // Stopbit, immer 1&lt;br /&gt;
    PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED an&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
int main (void) {&lt;br /&gt;
 &lt;br /&gt;
    char stringbuffer[64];  // Allgemeiner Puffer für Strings&lt;br /&gt;
    uint8_t buffer_full=0;  // noch ein Flag, aber nur in der Hauptschleife&lt;br /&gt;
    char * charpointer;     // Hilfszeiger&lt;br /&gt;
    &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
 &lt;br /&gt;
// Stringpuffer initialisieren&lt;br /&gt;
 &lt;br /&gt;
    stringbuffer[0] = &#039;\n&#039;;&lt;br /&gt;
    stringbuffer[1] = &#039;\r&#039;;&lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
 &lt;br /&gt;
        // &amp;quot;Sinnvolle&amp;quot; CPU Tätigkeit &lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
    &lt;br /&gt;
        // Wurde ein kompletter String empfangen &lt;br /&gt;
        // und der Buffer ist leer?&lt;br /&gt;
        if (uart_rx_flag==1 &amp;amp;&amp;amp; buffer_full==0) {    &lt;br /&gt;
            // ja, dann String lesen, &lt;br /&gt;
            // die ersten zwei Zeichen &lt;br /&gt;
            // aber nicht überschreiben&lt;br /&gt;
            get_string(stringbuffer+2);             &lt;br /&gt;
            buffer_full=1;&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        // Ist letzte Stringsendung abgeschlossen &lt;br /&gt;
        // und ein neuer String verfügbar?&lt;br /&gt;
        if (uart_tx_flag==1 &amp;amp;&amp;amp; buffer_full==1) {    &lt;br /&gt;
            // Newline + Carrige return anfügen&lt;br /&gt;
            strcat(stringbuffer, &amp;quot;\n\r&amp;quot;);           &lt;br /&gt;
            put_string(stringbuffer); // zurücksenden&lt;br /&gt;
            buffer_full=0; // Buffer ist wieder verfügbar&lt;br /&gt;
            // Alle Zeichen per LED morsen&lt;br /&gt;
            charpointer = stringbuffer;&lt;br /&gt;
            while(*charpointer) morse(*charpointer++);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
 &lt;br /&gt;
// hier werden Daten vom PC empfangen und in einem String zwischengespeichert&lt;br /&gt;
// Wird ein Stringterminator empfangen, wird ein Flag gesetzt, welches dem &lt;br /&gt;
// Hauptprogramm den kompletten Empfang signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    &lt;br /&gt;
    static uint8_t uart_rx_cnt;     // Zähler für empfangene Zeichen&lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // Daten auslesen, dadurch wird das Interruptflag gelöscht              &lt;br /&gt;
    data = UDR;&lt;br /&gt;
    &lt;br /&gt;
    // Ist Puffer frei für neue Daten? &lt;br /&gt;
    if (!uart_rx_flag) {&lt;br /&gt;
        // ja, ist Ende des Strings (RETURN) erreicht?&lt;br /&gt;
        if (data==&#039;\r&#039;) {&lt;br /&gt;
            // ja, dann String terminieren&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=0;              &lt;br /&gt;
            // Flag für &#039;Empfangspuffer voll&#039; setzen&lt;br /&gt;
            uart_rx_flag=1;&lt;br /&gt;
            // Zähler zurücksetzen&lt;br /&gt;
            uart_rx_cnt=0;&lt;br /&gt;
        }&lt;br /&gt;
        else if (uart_rx_cnt&amp;lt;(uart_buffer_size-1)) {     &lt;br /&gt;
            // Daten in Puffer speichern&lt;br /&gt;
            // aber durch if() Pufferüberlauf vermeiden&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=data;          &lt;br /&gt;
            uart_rx_cnt++; // Zähler erhöhen&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART TX data register empty interrupt&lt;br /&gt;
// hier werden neue Daten in das UART Senderegister geladen&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_UDRE_vect) {&lt;br /&gt;
    // Zeiger auf Sendepuffer&lt;br /&gt;
    static char* uart_tx_p = uart_tx_buffer;    &lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // zu sendendes Zeichen lesen, &lt;br /&gt;
    // Zeiger auf Sendepuffer erhöhen&lt;br /&gt;
    data = *uart_tx_p++;&lt;br /&gt;
    &lt;br /&gt;
    // Ende des nullterminierten Strings erreicht?&lt;br /&gt;
    if (data==0 ) {        &lt;br /&gt;
        UCSRB &amp;amp;= ~(1&amp;lt;&amp;lt;UDRIE);       // ja, dann UDRE Interrupt ausschalten        &lt;br /&gt;
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen&lt;br /&gt;
        uart_tx_flag = 1;           // Flag setzen, Übertragung beeendet&lt;br /&gt;
    }&lt;br /&gt;
    else UDR = data;                // nein, Daten senden&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie lange dauert meine Interruptroutine ? ===&lt;br /&gt;
&lt;br /&gt;
Diese Frage sollte man beantworten können, zumindest sollte eine Worst Case Abschätzung gemacht werden. Das geht auf zwei Wegen.&lt;br /&gt;
&lt;br /&gt;
* Simulation, dabei muss in einer verzweigten ISR der längste Pfad simuliert werden. Dazu müssen alle beteiligten Variablen auf den ensprechenden Wert gesetzt werden.&lt;br /&gt;
* Messung mit dem [[Oszilloskop]], dabei wird zum Beginn der ISR ein Pin auf HIGH gesetzt und am Ende auf LOW. Damit kann man in Echtzeit die Dauer der ISR messen. Die zusätzlichen Taktzyklen zum Aufruf und verlassen der ISR sind konstant und im wesentlichen bekannt. Mit einem modernen Digitaloszilloskop und dem &amp;quot;Infinite Persistence Mode&amp;quot; kann man eine Worst Case Messung vornehmen&lt;br /&gt;
&lt;br /&gt;
Als Hilfmittel zur Fehlersuche kann man auch am Ende der ISR prüfen, ob das jeweilige Interrupt-Request Bit schon wieder gesetzt ist. Wenn ja, dann ist die ISR in den meisten Fällen zu lang. Auch hier kann man einen Ausgang auf HIGH setzen und somit den Fehler anzeigen.&lt;br /&gt;
&lt;br /&gt;
==Interruptfeste Programmierung==&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Datenzugriff ===&lt;br /&gt;
&lt;br /&gt;
Von einem atomaren (engl. atomic) Datenzugriff spricht man, wenn der Zugriff innerhalb einer nicht unterbrechbaren Maschinenanweisung abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel Sorgfalt zu behandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
übersetzt sich auf AVR Prozessoren in&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  IN  r16,port&lt;br /&gt;
  ORI r16,0x03&lt;br /&gt;
  OUT port,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren, da der OUT Befehl den alten Zustand vor dem Interrupt wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gefährlich ist das insbesondere deshalb, weil der Fall nur selten auftritt und dies Verhalten sehr schlecht reproduzierbar ist.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei verschiedenen Prozessor-Architekturen tritt das Problem verschieden häufig auf. So übersetzt sich obiger Code bei MSP430 Prozessoren in einen einzelnen Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  OR  #0x03,port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
und stellt somit kein Problem dar. Im Zweifel hilft nur ein Blick in den erzeugten Assembler-Code. Bei der Übernahme fremden Codes ist dies zu beachten. Was bei i51 kein Problem war, kann bei AVR zu einem Problem werden, unter Umständen sogar abhängig von verwendeten Port sein.&lt;br /&gt;
&lt;br /&gt;
Ein ähnliches Problem entsteht bei Variablen, deren Grösse die Wortbreite der Maschine übersteigt. Bei 8-Bit-Prozessoren wie AVR oder i51 also bereits bei normalen &amp;quot;int&amp;quot; Variablen. Diese Variablen werden zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal versehentlich die Werte 0x01FF oder 0x0000 lesen.&lt;br /&gt;
&lt;br /&gt;
Dies ist auch ein Grund, weshalb für Programmierung auf derart &amp;quot;niedriger&amp;quot; Ebene Kenntnisse in Prozessorarchitektur und Assembler-Programmierung sehr hilfreich sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abhilfe&#039;&#039;&#039;: Wenn man sich nicht wirklich ganz sicher ist, sollten um kritische Aktivitäten herum jedesmal die Interrupts abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel ([[AVR-GCC]]):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  cli(); // Interrupts abschalten&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
  sei(); // Interrupts wieder einschalten&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man ein globales Einschalten der Interrupts mit sei() vermeiden will, kann man die folgende Methode benutzen. Hierbei werden die Interrupts nur eingeschaltet, wenn sie vorher bereits eingeschaltet waren (Hinweis aus der FAQ von avr-libc):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t sreg; // Lokale Sicherungskopie von SREG&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
  } &lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Prozessor kann man das Problem manchmal auch ohne Abschalten von Interrupts durch geeignete Programmierung lösen. So führt&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port = (port &amp;amp; ~0x0F) | lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
immer zum beschriebenen Problem,&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port &amp;amp;= ~0x0F;&lt;br /&gt;
  port |= lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
jedoch nicht, wenn die beiden Zeilen zu jeweils einem Assembler-Befehl übersetzt werden. Was dann aber abhängig von den Optimierungs-Einstellungen des Compilers werden kann. Eine Interrupt-feste Variante für AVR-Prozessoren der neuesten Generation, wie beispielsweise Tiny2313 und Mega88 (alle ab 2004):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  PINx = (PORTx &amp;amp; 0x0F) ^ lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reentrante Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine Funktion ist reentrant (wiedereintrittsfest), wenn sie mehrmals gleichzeitig aktiv sein kann, ohne dass sich diese Aufrufe gegenseitig beeinflussen. Betrifft beispielsweise Funktionen, die sowohl im Hauptprogramm als auch in Interrupts aufgerufen werden. Manche C Compiler erfordern eine besondere Kennzeichnung solcher Funktionen. Wenn möglich sollte man es jedoch vermeiden, eine Funktion aus dem Hauptprogramm &#039;&#039;&#039;und&#039;&#039;&#039; aus einem Interrupt aus aufzurufen. Das ist meist problemlos machbar.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
* [[AVR-Tutorial: Interrupts | AVR-Tutorial - Interrupts in Assembler]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts | AVR-GCC-Tutorial - Interrupts in C]]&lt;br /&gt;
* [[ADC]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=25215</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=25215"/>
		<updated>2007-12-25T18:52:43Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* CPC - Compare with Carry */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu 3 Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle&lt;br /&gt;
* die Flags im Statusregister&lt;br /&gt;
* bedingte Sprungbefehle&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstante. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags residieren im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können festzuhalten.&lt;br /&gt;
&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry Flag hält fest, ob ein Überlauf oder Unterlauf bei einer 8-Bit Berechnung statt gefunden hat.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero Flag hält fest, ob das Ergebnis der letzten 8-Bit Berechnung 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits der letzten 8-Bit Berechnung wieder. In 2-Komplement Arithmetik bedeutet ein gesetztes 7. Bit eine negative Zahl, daher der Name.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2-Komplement Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed Bit ist eine Verknüpfung aus dem N und dem V Flag. Es wird hauptsächlich für &#039;Signed&#039; Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half Carry Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4. Das Haupteinsatzgebiet ist der Bereich der BCD Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag wird von keiner Berechnung gesetzt, sondern steht zur ausschliesslichen Verwendung des Programmierers zur Verfügung. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt Flag hat ebenfalls nichts mit Vergleichen zu tun, sondern steuert ob Interrupts systemweit zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden durchgeführt. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann&lt;br /&gt;
&lt;br /&gt;
===CP - Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC - Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit grossen Variablen (16/32 Bit) benötigt. Siehe [[AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI - Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16..r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039; aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und alleine der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare Befehl wiederspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und logische [[Bitmanipulation | Verknüpfungen]]. Da dieses Information wichtig ist, ist auch in der bei Atmel erhältlichen [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
====BRSH - Branch if Same or Higher ====&lt;br /&gt;
&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; nicht gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
====BRLO - Branch if Lower ====&lt;br /&gt;
&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
====BRGE - Branch if Greater or Equal ====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Signed Flag (S)&#039;&#039;&#039; nicht gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; eingesetzt, so findet der Sprung dann und nur dann statt, wenn der zweite Operand größer oder gleich dem ersten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
====BRLT - Branch if Less Than ====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Signed Flag (S)&#039;&#039;&#039; gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der zweite Operand kleiner als der erste Operand ist.&lt;br /&gt;
&lt;br /&gt;
====BRMI - Branch if Minus====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Negativ Flag (N)&#039;&#039;&#039; gesetzt ist, das Ergbnis der letzen Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
====BRPL - Branch if Plus====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Negativ Flag (N)&#039;&#039;&#039; nicht gesetzt ist, das Ergbnis der letzen Operation also positiv war (einschiesslich Null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
====BREQ - Branch if Equal====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; gesetzt ist. Ist nach einem Vergleich das Zero Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
====BRNE - Branch if Not Equal====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; nicht gesetzt ist. Ist nach einem Vergleich das Zero Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
====BRCC - Branch if Carry Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit grossen Variablen (16/32 Bit) bzw. im Zusammenhang mit Schiebeoperatioen verwendet.&lt;br /&gt;
&lt;br /&gt;
====BRCS - Branch if Carry Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC.&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
====BRHC - Branch if Half Carry Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Half Carry Flag (H)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRHS - Branch if Half Carry Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Half Carry Flag (H)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRID - Branch if Global Interrupt is Disabled (Cleared)====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Interrupt Flag (I)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRIS - Branch if Global Interrupt is Enabled (Set)====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Interrupt Flag (I)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRTC - Branch if T Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;(T)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRTS - Branch if T Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;(T)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRVC - Branch if Overflow Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Overflow Flag (V)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRVS - Branch if Overflow Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Overflow Flag (V)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden abhängig zu machen. Sieht beispielweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, wenn im Register r17 der Zahlenwert 25 enthalten ist, dann lautet der Code&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zeig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18,123         ; hier stehen nun Anweisungen für den Fall&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, Größer, Kleiner zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;&#039;Zählschleife&#039;&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zaehlregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erricht ist&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich das Konstrukt umzudrehen. Anstatt von einem Startwert aus zu inkrementieren genügt es die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung gebrauch machen, das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; zu beeinflussen. Ist das Ergebnis des Dekrements 0, so wird das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; gesetzt, welches wiederum in der nachfolgenden &#039;&#039;&#039;BRNE&#039;&#039;&#039; Anweisung für einen bedingen Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungzeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=25214</id>
		<title>AVR-Tutorial: Mehrfachverzweigung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=25214"/>
		<updated>2007-12-25T17:46:44Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Einfacher Ansatz */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Oft ist es in einem Programm notwendig, eine Variable auf mehrere Werte zu prüfen und abhängig vom Ergebniss verschiedene Aktionen auszuösen. Diese Konstruktion nennt man Mehrfachverzweigung. In einem Struktogramm sieht das so aus.&lt;br /&gt;
&lt;br /&gt;
[[bild:mv_struktogramm.png]]&lt;br /&gt;
&lt;br /&gt;
In C gibt es direkt dafür eine Konstruktion namens &#039;&#039;&#039;switch&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
switch (variable) {&lt;br /&gt;
  case 1:       // Anweisungen für diesen Zeig, wenn variable == 1&lt;br /&gt;
    break;&lt;br /&gt;
  case 17:      // Anweisungen für diesen Zeig, wenn variable == 17&lt;br /&gt;
    break;&lt;br /&gt;
  case 33:      // Anweisungen für diesen Zeig, wenn variable == 33&lt;br /&gt;
    break;&lt;br /&gt;
  case 9:       // Anweisungen für diesen Zeig, wenn variable == 9&lt;br /&gt;
    break;&lt;br /&gt;
  case 22:      // Anweisungen für diesen Zeig, wenn variable == 22&lt;br /&gt;
    break;&lt;br /&gt;
  default:      // Anweisungen wenn keine der oben definierten Bedingungen erfüllt ist&lt;br /&gt;
    break;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Assembler muss man so etwas &amp;quot;zu Fuß&amp;quot; programmieren. Die verschiedenen Lösungen sollen hier betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
== Einfacher Ansatz ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall verwendet man eine lange Kette von &#039;&#039;&#039;cpi&#039;&#039;&#039; und &#039;&#039;&#039;breq&#039;&#039;&#039; Befehlen. Für jeden Zweig benötigt man zwei Befehle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Mehrfachverzeigung Version A&lt;br /&gt;
&lt;br /&gt;
; Einfacher Ansatz, mit vielen CPI&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    cpi     r16,1&lt;br /&gt;
    brne    zweig_0&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=1&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_0:&lt;br /&gt;
    cpi     r16,17&lt;br /&gt;
    brne    zweig_1&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=17&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_1:&lt;br /&gt;
    cpi     r16,33&lt;br /&gt;
    brne    zweig_2&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=33&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_2:&lt;br /&gt;
    cpi     r16,9&lt;br /&gt;
    brne    zweig_3&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=9&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_3:&lt;br /&gt;
    cpi     r16,22&lt;br /&gt;
    brne    kein_Treffer&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=22&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
kein_Treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 6*N Bytes (N = Anzahl der Zweige)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* leicht verständlich&lt;br /&gt;
* Es können beliebige Vergleichswerte geprüft werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* relativ hoher Programmspeicherbedarf&lt;br /&gt;
* die Größe der Zweige ist stark begrenzt, weil der Befehl &#039;&#039;&#039;breq&#039;&#039;&#039; maximal 63 Worte weit springen kann!&lt;br /&gt;
* die einzelnen Zweige haben unterschiedliche Durchlaufzeiten, der letzte Zweig ist am langsamsten&lt;br /&gt;
* nur bedingt übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
== Sprungtabelle ==&lt;br /&gt;
&lt;br /&gt;
Oft liegen die einzelnen Vergleichswerte nebeneinander (z.B. 7..15), z.B. bei der Übergabe von Parametern, Zustandsautomaten, Menueinträgen etc. . In so einem Fall kann man mittels einer &#039;&#039;&#039;Sprungtabelle&#039;&#039;&#039; das Programm verkürzen, beschleunigen und übersichtlicher gestalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; Mehrfachverzweigung Version B&lt;br /&gt;
&lt;br /&gt;
; Clevere Version mit Sprungtabelle&lt;br /&gt;
; minimum und maximum sind auf 0..255 begrenzt!&lt;br /&gt;
&lt;br /&gt;
.equ minimum = 3&lt;br /&gt;
.equ maximum = 7&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle)&lt;br /&gt;
    add     ZL,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     ZH,r16&lt;br /&gt;
    ijmp                                ; indirekter Sprung in Sprungtabelle&lt;br /&gt;
&lt;br /&gt;
kein_treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
    rjmp    zweig_0&lt;br /&gt;
    rjmp    zweig_1&lt;br /&gt;
    rjmp    zweig_2&lt;br /&gt;
    rjmp    zweig_3&lt;br /&gt;
    rjmp    zweig_4&lt;br /&gt;
&lt;br /&gt;
zweig_0:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_1:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Awneisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_2:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_3:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_4:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm weiter&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Programmbeschreibung &#039;&#039;&#039;&amp;lt;BR&amp;gt;&lt;br /&gt;
Wie ist dieses Programm nun zu verstehen? Das Prinzip beruht darauf, daß in einer gleichmässigen Tabelle Sprungbefehle auf einzelne Programmzweige abgelegt werden. Das ist praktisch genauso wie der AVR [[AVR-Tutorial: Interrupts | Interrupts]] verarbeitet. Über einen Index (0...N) wird ein Sprungbefehl ausgewählt und ausgeführt.Der entscheidende Befehl dazu ist &#039;&#039;&#039;ijmp&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Zunächst muss der Wertebereich, auf welchen die Variable geprüft werden soll (minimum bis maximum), normiert werden (0 bis (Maximum-Minimum)). Dazu wird einfach das Minimum subtrahiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss geprüft werden, ob der maximale Index nicht überschritten wird. Denn ein Sprung auf nichtexistierende Einträge oberhalb der Sprungtabelle wäre fatal!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss der indirekte Sprung vorbereitet werden. Dazu wird die Adresse der Sprungtabelle in das Z-Register geladen, welches ein 16 Bit Register ist und gleichbedeutend mit r30 und r31.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss der Index addiert werden, dies ist eine 16-Bit Addition.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    add     ZL,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     ZH,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt wird der indirekte Sprung in die Sprungtabelle ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ijmp                                ; indirekter Sprung in Sprungtabelle&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Sprungtabelle wird dann zum jeweiligen Zweig verzweigt.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
    rjmp    zweig_0&lt;br /&gt;
    rjmp    zweig_1&lt;br /&gt;
    rjmp    zweig_2&lt;br /&gt;
    rjmp    zweig_3&lt;br /&gt;
    rjmp    zweig_4&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zweig für einen ungültigen Index folgt direkt nach dem &#039;&#039;&#039;ijmp&#039;&#039;&#039;, weil der Befehl &#039;&#039;&#039;brsh&#039;&#039;&#039; nur maximal 63 Worte weit springen kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 2*N +18 Bytes (N = Anzahl der Zweige)&lt;br /&gt;
* maximale Gesamtgröße der Zweige wird durch den Befehl rjmp begrenzt (+/-4kB). Das sollte aber nur in sehr wenigen Fällen ein Problem sein (Man wird kaum einen AVR mit 8 kB FLASH mit einer einzigen Mehrfachverzweigung füllen!)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* relativ niedriger Programmspeicherbedarf&lt;br /&gt;
* die einzelnen Zweige haben unabhängig von der Grösse der Sprungtabelle eine konstante und kurze Durchlaufzeit von 12 Takten.&lt;br /&gt;
* übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* Die Vergleichswerte müssen lückenlos aufeinander folgen&lt;br /&gt;
&lt;br /&gt;
== Lange Sprungtabelle ==&lt;br /&gt;
Wenn man doch mal eine GIGA-Mehrfachverzeigung braucht, dann hilft die Version C.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m16def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; Mehrfachverzweigung Version C&lt;br /&gt;
&lt;br /&gt;
; Clevere Version mit langer Sprungtabelle&lt;br /&gt;
; funktioniert nur mit AVRs mit mehr als 8KB FLASH&lt;br /&gt;
; minimum und maximum sind auf 0..127 begrenzt!&lt;br /&gt;
&lt;br /&gt;
.equ minimum = 3&lt;br /&gt;
.equ maximum = 7&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
    lsl     r16                         ; Index mit 2 multiplizieren&lt;br /&gt;
    add     zl,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     zh,r16&lt;br /&gt;
    lpm     r16,Z+                      ; Low Byte laden und Pointer erhöhen&lt;br /&gt;
    lpm     ZH,Z                        ; zweites Byte laden&lt;br /&gt;
    mov     ZL,r16                      ; erstes Byte in Z-Pointer kopieren&lt;br /&gt;
    ijmp                                ; indirekter Sprung&lt;br /&gt;
&lt;br /&gt;
kein_treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
    jmp     ende_vergleich&lt;br /&gt;
&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
.dw zweig_0&lt;br /&gt;
.dw zweig_1&lt;br /&gt;
.dw zweig_2&lt;br /&gt;
.dw zweig_3&lt;br /&gt;
.dw zweig_4&lt;br /&gt;
&lt;br /&gt;
zweig_0:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_1:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Awneisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_2:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_3:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_4:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm weiter&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Programmbeschreibung &#039;&#039;&#039;&amp;lt;BR&amp;gt;&lt;br /&gt;
Diese Version ist der Version B sehr ähnlich. Der Unterschied besteht darin, daß in Version B die Sprungtabelle mit Sprungbefehlen gefüllt ist (rjmp) während in Version C die Startadressen der Funktionen ablegt sind. D.H. man kann nicht in die Sprungtabelle springen, sondern muss sich mit Hilfe des Index die richtige Adresse aus der Sprungtabelle lesen und mit &#039;&#039;&#039;ijmp&#039;&#039;&#039; anspringen. Klingt sehr ähnlich, ist aber dennoch verschieden.&lt;br /&gt;
&lt;br /&gt;
Die ersten drei Befehle sind identisch, es wird der Index normiert und auf das Maximum geprüft.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Die nächsten zwei Befehle laden wieder die Anfangsadresse der Sprungtabelle. Doch halt, hier wird die Adresse der Sprungtabelle mit zwei multipliziert. Des Rätsels Lösung gibt es weiter unten.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Der Index wird ebenfalls mit zwei multipliziert.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lsl     r16                         ; Index mit 2 multiplizieren&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Danach erfolgt eine 16-Bit Addition.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    add     zl,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     zh,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Nun zeigt unser Z-Zeiger auf den richtigen Tabelleneintrag. Jetzt müssen zwei Bytes aus dem FLASH geladen werden. Das geschieht mit Hilfe des &#039;&#039;&#039;lpm&#039;&#039;&#039;-Befehls (&#039;&#039;&#039;L&#039;&#039;&#039;oad &#039;&#039;&#039;P&#039;&#039;&#039;rogram &#039;&#039;&#039;M&#039;&#039;&#039;emory). Hier wird die erweiterte Version des lpm-Befehls verwendet, wie sie nur auf grösseren AVRs verfügbar ist. Dabei wird ein Byte in Register r16 geladen und gleichzeitig der Z-Pointer um eins erhöht. Damit zeigt er wunderbar auf das nächste Byte, welches auch geladen werden muss.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lpm     r16,Z+                      ; Low Byte laden und Zeiger erhöhen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Der zweite lpm-Befehl ist etwas ungewöhnlich, denn er überschreibt einen Teil des Z-Pointers! In den meisten Programmen wäre das ein Schuss ins Knie (Programmierfehler!), da wir aber den Z-Pointer danach sowieso mit neuen Daten laden ist das OK.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lpm     ZH,Z                        ; zweites Byte laden&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Das zuerst gelesene Byte wird in den Z-Pointer kopiert. Nun steht die Startadresse des gewählten Zweigs im Z-Pointer.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    mov     ZL,r16                      ; erstes Byte in Z-zeiger kopieren&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Zu guter Letzt wird der indirekte Sprung ausgeführt und bringt uns direkt in den Programmzweig.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ijmp                                ; indirekter Sprung direkt in den Programmzweig&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zweig für einen ungültigen Index folgt direkt nach dem &#039;&#039;&#039;ijmp&#039;&#039;&#039;, weil der Befehl &#039;&#039;&#039;brsh&#039;&#039;&#039; nur maximal 63 Worte weit springen kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 2*N +26 Bytes (N = Anzahl der Zweige)&lt;br /&gt;
* unbegrenzte Sprungweite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* relativ niedriger Programmspeicherbedarf&lt;br /&gt;
* die einzelnen Zweige haben unabhängig von der Grösse der Sprungtabelle eine konstante und kurze Durchlaufzeit von 18 Takten&lt;br /&gt;
* übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* Die Vergleichswerte müssen lückenlos aufeinander folgen&lt;br /&gt;
* geringfügig höherer Programmspeicherbedarf (8 Byte mehr) und grössere Durchlaufzeit (6 Takte mehr)als Version B&lt;br /&gt;
&lt;br /&gt;
== Z-Pointer leicht verständlich ==&lt;br /&gt;
&lt;br /&gt;
Auf den ersten Blick scheint es sonderbar, daß Version B die Adresse der Sprungtabelle direkt lädt, während Version C sowohl Anfangsadresse als auch Index mit zwei multipliziert. Warum ist das so?&lt;br /&gt;
&lt;br /&gt;
Version B verwendet nur den Befehl &#039;&#039;&#039;ijmp&#039;&#039;&#039;. Dieser erwartet im Z-Register eine Adresse zur Programmausführung, eine &#039;&#039;&#039;Wort-Adresse&#039;&#039;&#039;. Da der Programmspeicher des AVR 16 Bit breit ist (=1 Wort = 2 Bytes), werden nur Worte adressiert, nicht jedoch Bytes! Genauso arbeitet der Assembler. Jedes Label entspricht einer Wort-Adresse. Damit kann man mit einer 12 Bit-Adresse 4096 Worte adressieren (=8192 Bytes). Wenn man sich die Befehle der einzelnen AVRs anschaut wird klar, daß alle AVRs mit 8KB und weniger FLASH nur die Befehle rjmp und rcall besitzen. Denn sie brauchen nicht mehr! Mit 12 Adressbits, welche direkt in einem Wort im Befehl rjmp bzw. rcall kodiert sind, kann der gesamte Programmspeicher erreicht werden. Größere AVRs besitzen call und jmp, dort ist die Adresse als 22 bzw. 16 Bit Zahl kodiert, deshalb brauchen diese Befehle auch 2 Worte Programmspeicher.&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; dient zum laden einzelner Bytes aus dem Programmspeicher. Das ist vor allem für Tabellen mit konstanten Werten sehr nützlich (7-Segmentdekoder, Zeichensätze, Kennlinien, Parameter Texte, etc.) Doch wie kommt man nun in dem wortweise adressierten Programmspeicher an einzelne Bytes? Ganz einfach. Der AVR &amp;quot;mogelt&amp;quot; hier und erwartet im Z-Register eine &#039;&#039;&#039;Byte-Adresse&#039;&#039;&#039;. Von dieser Adresse bilden die Bits 15..1 die Wortadresse, welche zur Adressierung des Programmspeichers verwendet wird. Bit 0 entscheidet dann, ob das hoch- oder niederwertige Byte in das Zielregister kopiert werden soll (0=niederwertiges Byte; 1=höherwertiges Byte).&lt;br /&gt;
&lt;br /&gt;
Darum muss bei Verwendung des Befehls lpm die Anfangsadresse immer mit zwei multipliziert werden.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
In Version C muss zusätzlich der Index mit zwei multipliziert werden, weil jeder Tabelleneintrag (Adresse des Programmzweigs) ein Wort breit ist. Damit wird aus einem Index von 0,1,2,3,4 ein Offset von 0,2,4,6,8.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Vergleiche|&lt;br /&gt;
zurücklink=AVR-Tutorial: Vergleiche|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=UART|&lt;br /&gt;
vorlink=AVR-Tutorial: UART}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=25213</id>
		<title>AVR-Tutorial: Mehrfachverzweigung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Mehrfachverzweigung&amp;diff=25213"/>
		<updated>2007-12-25T17:43:53Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Einfacher Ansatz */ Beispiel verbessert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Oft ist es in einem Programm notwendig, eine Variable auf mehrere Werte zu prüfen und abhängig vom Ergebniss verschiedene Aktionen auszuösen. Diese Konstruktion nennt man Mehrfachverzweigung. In einem Struktogramm sieht das so aus.&lt;br /&gt;
&lt;br /&gt;
[[bild:mv_struktogramm.png]]&lt;br /&gt;
&lt;br /&gt;
In C gibt es direkt dafür eine Konstruktion namens &#039;&#039;&#039;switch&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
switch (variable) {&lt;br /&gt;
  case 1:       // Anweisungen für diesen Zeig, wenn variable == 1&lt;br /&gt;
    break;&lt;br /&gt;
  case 17:      // Anweisungen für diesen Zeig, wenn variable == 17&lt;br /&gt;
    break;&lt;br /&gt;
  case 33:      // Anweisungen für diesen Zeig, wenn variable == 33&lt;br /&gt;
    break;&lt;br /&gt;
  case 9:       // Anweisungen für diesen Zeig, wenn variable == 9&lt;br /&gt;
    break;&lt;br /&gt;
  case 22:      // Anweisungen für diesen Zeig, wenn variable == 22&lt;br /&gt;
    break;&lt;br /&gt;
  default:      // Anweisungen wenn keine der oben definierten Bedingungen erfüllt ist&lt;br /&gt;
    break;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Assembler muss man so etwas &amp;quot;zu Fuß&amp;quot; programmieren. Die verschiedenen Lösungen sollen hier betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
== Einfacher Ansatz ==&lt;br /&gt;
&lt;br /&gt;
Im einfachsten Fall verwendet man eine lange Kette von &#039;&#039;&#039;cpi&#039;&#039;&#039; und &#039;&#039;&#039;breq&#039;&#039;&#039; Befehlen. Für jeden Zweig benötigt man zwei Befehle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; Mehrfachverzeigung Version A&lt;br /&gt;
&lt;br /&gt;
; Einfacher Ansatz, mit vielen CPI&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    cpi     r16,7&lt;br /&gt;
    brne    zweig_0&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=7&lt;br /&gt;
&lt;br /&gt;
zweig_0:&lt;br /&gt;
    cpi     r16,5&lt;br /&gt;
    brne    zweig_1&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=5&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_1:&lt;br /&gt;
    cpi     r16,28&lt;br /&gt;
    brne    zweig_2&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=28&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_2:&lt;br /&gt;
    cpi     r16,16&lt;br /&gt;
    brne    zweig_3&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=16&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
zweig_3:&lt;br /&gt;
    cpi     r16,1&lt;br /&gt;
    brne    kein_Treffer&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig r16=1&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
kein_Treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 6*N Bytes (N = Anzahl der Zweige)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* leicht verständlich&lt;br /&gt;
* Es können beliebige Vergleichswerte geprüft werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* relativ hoher Programmspeicherbedarf&lt;br /&gt;
* die Größe der Zweige ist stark begrenzt, weil der Befehl &#039;&#039;&#039;breq&#039;&#039;&#039; maximal 63 Worte weit springen kann!&lt;br /&gt;
* die einzelnen Zweige haben unterschiedliche Durchlaufzeiten, der letzte Zweig ist am langsamsten&lt;br /&gt;
* nur bedingt übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
== Sprungtabelle ==&lt;br /&gt;
&lt;br /&gt;
Oft liegen die einzelnen Vergleichswerte nebeneinander (z.B. 7..15), z.B. bei der Übergabe von Parametern, Zustandsautomaten, Menueinträgen etc. . In so einem Fall kann man mittels einer &#039;&#039;&#039;Sprungtabelle&#039;&#039;&#039; das Programm verkürzen, beschleunigen und übersichtlicher gestalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; Mehrfachverzweigung Version B&lt;br /&gt;
&lt;br /&gt;
; Clevere Version mit Sprungtabelle&lt;br /&gt;
; minimum und maximum sind auf 0..255 begrenzt!&lt;br /&gt;
&lt;br /&gt;
.equ minimum = 3&lt;br /&gt;
.equ maximum = 7&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle)&lt;br /&gt;
    add     ZL,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     ZH,r16&lt;br /&gt;
    ijmp                                ; indirekter Sprung in Sprungtabelle&lt;br /&gt;
&lt;br /&gt;
kein_treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
    rjmp    zweig_0&lt;br /&gt;
    rjmp    zweig_1&lt;br /&gt;
    rjmp    zweig_2&lt;br /&gt;
    rjmp    zweig_3&lt;br /&gt;
    rjmp    zweig_4&lt;br /&gt;
&lt;br /&gt;
zweig_0:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_1:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Awneisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_2:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_3:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_4:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm weiter&lt;br /&gt;
&lt;br /&gt;
    rjmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Programmbeschreibung &#039;&#039;&#039;&amp;lt;BR&amp;gt;&lt;br /&gt;
Wie ist dieses Programm nun zu verstehen? Das Prinzip beruht darauf, daß in einer gleichmässigen Tabelle Sprungbefehle auf einzelne Programmzweige abgelegt werden. Das ist praktisch genauso wie der AVR [[AVR-Tutorial: Interrupts | Interrupts]] verarbeitet. Über einen Index (0...N) wird ein Sprungbefehl ausgewählt und ausgeführt.Der entscheidende Befehl dazu ist &#039;&#039;&#039;ijmp&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Zunächst muss der Wertebereich, auf welchen die Variable geprüft werden soll (minimum bis maximum), normiert werden (0 bis (Maximum-Minimum)). Dazu wird einfach das Minimum subtrahiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss geprüft werden, ob der maximale Index nicht überschritten wird. Denn ein Sprung auf nichtexistierende Einträge oberhalb der Sprungtabelle wäre fatal!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss der indirekte Sprung vorbereitet werden. Dazu wird die Adresse der Sprungtabelle in das Z-Register geladen, welches ein 16 Bit Register ist und gleichbedeutend mit r30 und r31.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle)       ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach muss der Index addiert werden, dies ist eine 16-Bit Addition.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    add     ZL,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     ZH,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu guter Letzt wird der indirekte Sprung in die Sprungtabelle ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ijmp                                ; indirekter Sprung in Sprungtabelle&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Sprungtabelle wird dann zum jeweiligen Zweig verzweigt.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
    rjmp    zweig_0&lt;br /&gt;
    rjmp    zweig_1&lt;br /&gt;
    rjmp    zweig_2&lt;br /&gt;
    rjmp    zweig_3&lt;br /&gt;
    rjmp    zweig_4&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zweig für einen ungültigen Index folgt direkt nach dem &#039;&#039;&#039;ijmp&#039;&#039;&#039;, weil der Befehl &#039;&#039;&#039;brsh&#039;&#039;&#039; nur maximal 63 Worte weit springen kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 2*N +18 Bytes (N = Anzahl der Zweige)&lt;br /&gt;
* maximale Gesamtgröße der Zweige wird durch den Befehl rjmp begrenzt (+/-4kB). Das sollte aber nur in sehr wenigen Fällen ein Problem sein (Man wird kaum einen AVR mit 8 kB FLASH mit einer einzigen Mehrfachverzweigung füllen!)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* relativ niedriger Programmspeicherbedarf&lt;br /&gt;
* die einzelnen Zweige haben unabhängig von der Grösse der Sprungtabelle eine konstante und kurze Durchlaufzeit von 12 Takten.&lt;br /&gt;
* übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* Die Vergleichswerte müssen lückenlos aufeinander folgen&lt;br /&gt;
&lt;br /&gt;
== Lange Sprungtabelle ==&lt;br /&gt;
Wenn man doch mal eine GIGA-Mehrfachverzeigung braucht, dann hilft die Version C.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m16def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
; Mehrfachverzweigung Version C&lt;br /&gt;
&lt;br /&gt;
; Clevere Version mit langer Sprungtabelle&lt;br /&gt;
; funktioniert nur mit AVRs mit mehr als 8KB FLASH&lt;br /&gt;
; minimum und maximum sind auf 0..127 begrenzt!&lt;br /&gt;
&lt;br /&gt;
.equ minimum = 3&lt;br /&gt;
.equ maximum = 7&lt;br /&gt;
&lt;br /&gt;
start_vergleich:&lt;br /&gt;
&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
    lsl     r16                         ; Index mit 2 multiplizieren&lt;br /&gt;
    add     zl,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     zh,r16&lt;br /&gt;
    lpm     r16,Z+                      ; Low Byte laden und Pointer erhöhen&lt;br /&gt;
    lpm     ZH,Z                        ; zweites Byte laden&lt;br /&gt;
    mov     ZL,r16                      ; erstes Byte in Z-Pointer kopieren&lt;br /&gt;
    ijmp                                ; indirekter Sprung&lt;br /&gt;
&lt;br /&gt;
kein_treffer:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für den Fall, dass keiner der Vergleiche erfolgreich war&lt;br /&gt;
&lt;br /&gt;
    jmp     ende_vergleich&lt;br /&gt;
&lt;br /&gt;
Sprungtabelle:&lt;br /&gt;
.dw zweig_0&lt;br /&gt;
.dw zweig_1&lt;br /&gt;
.dw zweig_2&lt;br /&gt;
.dw zweig_3&lt;br /&gt;
.dw zweig_4&lt;br /&gt;
&lt;br /&gt;
zweig_0:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_1:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Awneisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_2:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_3:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich&lt;br /&gt;
&lt;br /&gt;
zweig_4:&lt;br /&gt;
&lt;br /&gt;
; hier stehen jetzt alle Anweisungen für diesen Zweig&lt;br /&gt;
&lt;br /&gt;
ende_vergleich:&lt;br /&gt;
&lt;br /&gt;
; hier geht das Programm weiter&lt;br /&gt;
&lt;br /&gt;
    jmp    ende_vergleich          ; nur für Simulationszwecke! ENTFERNEN!&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Programmbeschreibung &#039;&#039;&#039;&amp;lt;BR&amp;gt;&lt;br /&gt;
Diese Version ist der Version B sehr ähnlich. Der Unterschied besteht darin, daß in Version B die Sprungtabelle mit Sprungbefehlen gefüllt ist (rjmp) während in Version C die Startadressen der Funktionen ablegt sind. D.H. man kann nicht in die Sprungtabelle springen, sondern muss sich mit Hilfe des Index die richtige Adresse aus der Sprungtabelle lesen und mit &#039;&#039;&#039;ijmp&#039;&#039;&#039; anspringen. Klingt sehr ähnlich, ist aber dennoch verschieden.&lt;br /&gt;
&lt;br /&gt;
Die ersten drei Befehle sind identisch, es wird der Index normiert und auf das Maximum geprüft.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    subi    r16,minimum                 ; Nullpunkt verschieben&lt;br /&gt;
    cpi     r16,(maximum-minimum+1)     ; Index auf Maximum prüfen&lt;br /&gt;
    brsh    kein_Treffer                ; Index zu gross -&amp;gt; Fehler&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Die nächsten zwei Befehle laden wieder die Anfangsadresse der Sprungtabelle. Doch halt, hier wird die Adresse der Sprungtabelle mit zwei multipliziert. Des Rätsels Lösung gibt es weiter unten.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Der Index wird ebenfalls mit zwei multipliziert.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lsl     r16                         ; Index mit 2 multiplizieren&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Danach erfolgt eine 16-Bit Addition.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    add     zl,r16                      ; Index addieren, 16 Bit&lt;br /&gt;
    ldi     r16,0                   &lt;br /&gt;
    adc     zh,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Nun zeigt unser Z-Zeiger auf den richtigen Tabelleneintrag. Jetzt müssen zwei Bytes aus dem FLASH geladen werden. Das geschieht mit Hilfe des &#039;&#039;&#039;lpm&#039;&#039;&#039;-Befehls (&#039;&#039;&#039;L&#039;&#039;&#039;oad &#039;&#039;&#039;P&#039;&#039;&#039;rogram &#039;&#039;&#039;M&#039;&#039;&#039;emory). Hier wird die erweiterte Version des lpm-Befehls verwendet, wie sie nur auf grösseren AVRs verfügbar ist. Dabei wird ein Byte in Register r16 geladen und gleichzeitig der Z-Pointer um eins erhöht. Damit zeigt er wunderbar auf das nächste Byte, welches auch geladen werden muss.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lpm     r16,Z+                      ; Low Byte laden und Zeiger erhöhen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Der zweite lpm-Befehl ist etwas ungewöhnlich, denn er überschreibt einen Teil des Z-Pointers! In den meisten Programmen wäre das ein Schuss ins Knie (Programmierfehler!), da wir aber den Z-Pointer danach sowieso mit neuen Daten laden ist das OK.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    lpm     ZH,Z                        ; zweites Byte laden&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Das zuerst gelesene Byte wird in den Z-Pointer kopiert. Nun steht die Startadresse des gewählten Zweigs im Z-Pointer.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    mov     ZL,r16                      ; erstes Byte in Z-zeiger kopieren&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Zu guter Letzt wird der indirekte Sprung ausgeführt und bringt uns direkt in den Programmzweig.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ijmp                                ; indirekter Sprung direkt in den Programmzweig&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Zweig für einen ungültigen Index folgt direkt nach dem &#039;&#039;&#039;ijmp&#039;&#039;&#039;, weil der Befehl &#039;&#039;&#039;brsh&#039;&#039;&#039; nur maximal 63 Worte weit springen kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eigenschaften&#039;&#039;&#039;&lt;br /&gt;
* Programmspeicherbedarf: 2*N +26 Bytes (N = Anzahl der Zweige)&lt;br /&gt;
* unbegrenzte Sprungweite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vorteile&#039;&#039;&#039;&lt;br /&gt;
* relativ niedriger Programmspeicherbedarf&lt;br /&gt;
* die einzelnen Zweige haben unabhängig von der Grösse der Sprungtabelle eine konstante und kurze Durchlaufzeit von 18 Takten&lt;br /&gt;
* übersichtlicher Quellcode&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachteile&#039;&#039;&#039;&lt;br /&gt;
* Die Vergleichswerte müssen lückenlos aufeinander folgen&lt;br /&gt;
* geringfügig höherer Programmspeicherbedarf (8 Byte mehr) und grössere Durchlaufzeit (6 Takte mehr)als Version B&lt;br /&gt;
&lt;br /&gt;
== Z-Pointer leicht verständlich ==&lt;br /&gt;
&lt;br /&gt;
Auf den ersten Blick scheint es sonderbar, daß Version B die Adresse der Sprungtabelle direkt lädt, während Version C sowohl Anfangsadresse als auch Index mit zwei multipliziert. Warum ist das so?&lt;br /&gt;
&lt;br /&gt;
Version B verwendet nur den Befehl &#039;&#039;&#039;ijmp&#039;&#039;&#039;. Dieser erwartet im Z-Register eine Adresse zur Programmausführung, eine &#039;&#039;&#039;Wort-Adresse&#039;&#039;&#039;. Da der Programmspeicher des AVR 16 Bit breit ist (=1 Wort = 2 Bytes), werden nur Worte adressiert, nicht jedoch Bytes! Genauso arbeitet der Assembler. Jedes Label entspricht einer Wort-Adresse. Damit kann man mit einer 12 Bit-Adresse 4096 Worte adressieren (=8192 Bytes). Wenn man sich die Befehle der einzelnen AVRs anschaut wird klar, daß alle AVRs mit 8KB und weniger FLASH nur die Befehle rjmp und rcall besitzen. Denn sie brauchen nicht mehr! Mit 12 Adressbits, welche direkt in einem Wort im Befehl rjmp bzw. rcall kodiert sind, kann der gesamte Programmspeicher erreicht werden. Größere AVRs besitzen call und jmp, dort ist die Adresse als 22 bzw. 16 Bit Zahl kodiert, deshalb brauchen diese Befehle auch 2 Worte Programmspeicher.&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039; dient zum laden einzelner Bytes aus dem Programmspeicher. Das ist vor allem für Tabellen mit konstanten Werten sehr nützlich (7-Segmentdekoder, Zeichensätze, Kennlinien, Parameter Texte, etc.) Doch wie kommt man nun in dem wortweise adressierten Programmspeicher an einzelne Bytes? Ganz einfach. Der AVR &amp;quot;mogelt&amp;quot; hier und erwartet im Z-Register eine &#039;&#039;&#039;Byte-Adresse&#039;&#039;&#039;. Von dieser Adresse bilden die Bits 15..1 die Wortadresse, welche zur Adressierung des Programmspeichers verwendet wird. Bit 0 entscheidet dann, ob das hoch- oder niederwertige Byte in das Zielregister kopiert werden soll (0=niederwertiges Byte; 1=höherwertiges Byte).&lt;br /&gt;
&lt;br /&gt;
Darum muss bei Verwendung des Befehls lpm die Anfangsadresse immer mit zwei multipliziert werden.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     ZL,low(Sprungtabelle*2)     ; Tabellenzeiger laden, 16 Bit&lt;br /&gt;
    ldi     ZH,high(Sprungtabelle*2)&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
In Version C muss zusätzlich der Index mit zwei multipliziert werden, weil jeder Tabelleneintrag (Adresse des Programmzweigs) ein Wort breit ist. Damit wird aus einem Index von 0,1,2,3,4 ein Offset von 0,2,4,6,8.&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Vergleiche|&lt;br /&gt;
zurücklink=AVR-Tutorial: Vergleiche|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=UART|&lt;br /&gt;
vorlink=AVR-Tutorial: UART}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=25212</id>
		<title>AVR-Tutorial: Vergleiche</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Vergleiche&amp;diff=25212"/>
		<updated>2007-12-25T17:40:49Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Kleine Überarbeitung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Vergleiche und Entscheidungen sind in jeder Programmiersprache ein zentrales Mittel um den Programmfluss abhängig von Bedingungen zu kontrollieren. In einem [[AVR]] spielen dazu 3 Komponenten zusammen:&lt;br /&gt;
* Vergleichsbefehle&lt;br /&gt;
* die Flags im Statusregister&lt;br /&gt;
* bedingte Sprungbefehle&lt;br /&gt;
&lt;br /&gt;
Der Zusammenhang ist dabei folgender: Die Vergleichsbefehle führen einen Vergleich durch, zum Beispiel zwischen zwei Registern oder zwischen einem Register und einer Konstante. Das Ergebnis des Vergleiches wird in den Flags abgelegt. Die bedingten Sprungbefehle werten die Flags aus und führen bei einem positiven Ergebnis den Sprung aus. Besonders der erste Satzteil ist wichtig! Den bedingten Sprungbefehlen ist es nämlich völlig egal, ob die Flags über Vergleichsbefehle oder über sonstige Befehle gesetzt wurden. Die Sprungbefehle werten einfach nur die Flags aus, wie auch immer diese zu ihrem Zustand kommen.&lt;br /&gt;
&lt;br /&gt;
==Flags==&lt;br /&gt;
&lt;br /&gt;
Die Flags residieren im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039;. Ihre Aufgabe ist es, das Auftreten bestimmter Ereignisse, die während Berechnungen eintreten können festzuhalten.&lt;br /&gt;
&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | I | T | H | S | V | N | Z | C |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+&lt;br /&gt;
&lt;br /&gt;
===Carry (C)===&lt;br /&gt;
Das Carry Flag hält fest, ob ein Überlauf oder Unterlauf bei einer 8-Bit Berechnung statt gefunden hat.&lt;br /&gt;
&lt;br /&gt;
===Zero (Z)===&lt;br /&gt;
Das Zero Flag hält fest, ob das Ergebnis der letzten 8-Bit Berechnung 0 war oder nicht.&lt;br /&gt;
&lt;br /&gt;
===Negative (N)===&lt;br /&gt;
Spiegelt den Zustand des höchstwertigen Bits der letzten 8-Bit Berechnung wieder. In 2-Komplement Arithmetik bedeutet ein gesetztes 7. Bit eine negative Zahl, daher der Name.&lt;br /&gt;
&lt;br /&gt;
===Overflow (V)===&lt;br /&gt;
Dieses Bit wird gesetzt, wenn bei einer Berechnung mit 2-Komplement Arithmetik ein Überlauf (Unterlauf) stattgefunden hat. Dies entspricht einem Überlauf von Bit 6 ins Bit 7&lt;br /&gt;
&lt;br /&gt;
===Signed (S)===&lt;br /&gt;
Das Signed Bit ist eine Verknüpfung aus dem N und dem V Flag. Es wird hauptsächlich für &#039;Signed&#039; Tests benötigt. Daher auch der Name.&lt;br /&gt;
&lt;br /&gt;
===Half Carry (H)===&lt;br /&gt;
Das Half Carry Flag hat die gleiche Aufgabe wie das Carry Flag, nur beschäftigt es sich mit einem Überlauf von Bit 3 nach Bit 4. Das Haupteinsatzgebiet ist der Bereich der BCD Arithmetik.&lt;br /&gt;
&lt;br /&gt;
=== Transfer (T)===&lt;br /&gt;
Das T-Flag wird von keiner Berechnung gesetzt, sondern steht zur ausschliesslichen Verwendung des Programmierers zur Verfügung. Damit können Bits von einer Stelle schnell an eine andere kopiert oder getestet werden.&lt;br /&gt;
&lt;br /&gt;
===Interrupt (I)===&lt;br /&gt;
Das Interrupt Flag hat ebenfalls nichts mit Vergleichen zu tun, sondern steuert ob Interrupts systemweit zugelassen sind (siehe [[AVR-Tutorial: Interrupts]]).&lt;br /&gt;
&lt;br /&gt;
==Vergleiche==&lt;br /&gt;
Um einen Vergleich durchzuführen, wird intern eine Subtraktion der beiden Operanden durchgeführt. Das eigentliche Ergebnis der Subtraktion wird allerdings verworfen, es bleibt nur die neue Belegung der Flags übrig, die in weiterer Folge ausgewertet werden kann&lt;br /&gt;
&lt;br /&gt;
===CP - Compare===&lt;br /&gt;
Vergleicht den Inhalt zweier Register miteinander. Prozessorintern wird dabei eine Subtraktion der beiden Register durchgeführt. Das eigentliche Subtraktionsergebnis wird allerdings verworfen, das Subtraktionsergebnis beeinflusst lediglich die Flags.&lt;br /&gt;
&lt;br /&gt;
===CPC - Compare with Carry===&lt;br /&gt;
Vergleicht den Inhalt zweier Register, wobei das Carry Flag in den Vergleich mit einbezogen wird. Dieser Befehl wird für Arithmetik mit grossen Variablen (16/32 Bit) benötigt. Siehe [[	&lt;br /&gt;
AVR-Tutorial: Arithmetik]].&lt;br /&gt;
&lt;br /&gt;
===CPI - Compare Immediate===&lt;br /&gt;
Vergleicht den Inhalt eines Registers mit einer direkt angegebenen Konstanten. Der Befehl ist nur auf die Register r16..r31 anwendbar.&lt;br /&gt;
&lt;br /&gt;
==Bedingte Sprünge==&lt;br /&gt;
&lt;br /&gt;
Die bedingten Sprünge werten immer bestimmte Flags im Statusregister &#039;&#039;&#039;SREG&#039;&#039;&#039; aus. Es spielt dabei keine Rolle, ob dies nach einem Vergleichsbefehl oder einem sonstigen Befehl gemacht wird. Entscheidend ist einzig und alleine der Zustand des abgefragten Flags. Die Namen der Sprungbefehle wurden allerdings so gewählt, daß sich im Befehlsnamen die Beziehung der Operanden direkt nach einem Compare Befehl wiederspiegelt. Zu beachten ist auch, daß die Flags nicht nur durch Vergleichsbefehle verändert werden, sondern auch durch arithmetische Operationen, Schiebebefehle und logische [[Bitmanipulation | Verknüpfungen]]. Da dieses Information wichtig ist, ist auch in der bei Atmel erhältlichen [http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf Übersicht über alle Assemblerbefehle] bei jedem Befehl angegeben, ob und wie er Flags beeinflusst. Ebenso ist dort eine kompakte Übersicht aller bedingten Sprünge zu finden. Beachten muss man jedoch, dass die bedingten Sprünge maximal 64 Worte weit springen können.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenlose Zahlen ===&lt;br /&gt;
&lt;br /&gt;
====BRSH - Branch if Same or Higher ====&lt;br /&gt;
&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; nicht gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand größer oder gleich dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
====BRLO - Branch if Lower ====&lt;br /&gt;
&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann statt, wenn der erste Operand kleiner dem zweiten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Sprünge für vorzeichenbehaftete Zahlen ===&lt;br /&gt;
&lt;br /&gt;
====BRGE - Branch if Greater or Equal ====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Signed Flag (S)&#039;&#039;&#039; nicht gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; eingesetzt, so findet der Sprung dann und nur dann statt, wenn der zweite Operand größer oder gleich dem ersten Operanden ist.&lt;br /&gt;
&lt;br /&gt;
====BRLT - Branch if Less Than ====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Signed Flag (S)&#039;&#039;&#039; gesetzt ist. Wird dieser Branch direkt nach einer &#039;&#039;&#039;CP&#039;&#039;&#039;, &#039;&#039;&#039;CPI&#039;&#039;&#039;, &#039;&#039;&#039;SUB&#039;&#039;&#039; oder &#039;&#039;&#039;SUBI&#039;&#039;&#039; Operation eingesetzt, so findet der Sprung dann und nur dann statt, wenn der zweite Operand kleiner als der erste Operand ist.&lt;br /&gt;
&lt;br /&gt;
====BRMI - Branch if Minus====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Negativ Flag (N)&#039;&#039;&#039; gesetzt ist, das Ergbnis der letzen Operation also negativ war.&lt;br /&gt;
&lt;br /&gt;
====BRPL - Branch if Plus====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Negativ Flag (N)&#039;&#039;&#039; nicht gesetzt ist, das Ergbnis der letzen Operation also positiv war (einschiesslich Null).&lt;br /&gt;
&lt;br /&gt;
=== Sonstige bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
====BREQ - Branch if Equal====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; gesetzt ist. Ist nach einem Vergleich das Zero Flag gesetzt, lieferte die interne Subtraktion also 0, so waren beide Operanden gleich.&lt;br /&gt;
&lt;br /&gt;
====BRNE - Branch if Not Equal====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; nicht gesetzt ist. Ist nach einem Vergleich das Zero Flag nicht gesetzt, lieferte die interne Subtraktion also nicht 0, so waren beide Operanden verschieden.&lt;br /&gt;
&lt;br /&gt;
====BRCC - Branch if Carry Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; nicht gesetzt ist. Dieser Befehl wird oft für Arithmetik mit grossen Variablen (16/32 Bit) bzw. im Zusammenhang mit Schiebeoperatioen verwendet.&lt;br /&gt;
&lt;br /&gt;
====BRCS - Branch if Carry Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Carry Flag (C)&#039;&#039;&#039; gesetzt ist. Die Verwendung ist sehr ähnlich zu BRCC.&lt;br /&gt;
&lt;br /&gt;
=== Selten verwendete bedingte Sprünge ===&lt;br /&gt;
&lt;br /&gt;
====BRHC - Branch if Half Carry Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Half Carry Flag (H)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRHS - Branch if Half Carry Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Half Carry Flag (H)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRID - Branch if Global Interrupt is Disabled (Cleared)====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Interrupt Flag (I)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRIS - Branch if Global Interrupt is Enabled (Set)====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Interrupt Flag (I)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRTC - Branch if T Flag is Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;(T)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRTS - Branch if T Flag is Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;(T)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRVC - Branch if Overflow Cleared====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Overflow Flag (V)&#039;&#039;&#039; nicht gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
====BRVS - Branch if Overflow Set====&lt;br /&gt;
Der Sprung wird durchgeführt, wenn das &#039;&#039;&#039;Overflow Flag (V)&#039;&#039;&#039; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungen ===&lt;br /&gt;
&lt;br /&gt;
In jedem Programm kommt früher oder später das Problem, die Ausführung von Codeteilen von irgendwelchen Zahlenwerten, die sich in anderen Registern befinden abhängig zu machen. Sieht beispielweise die Aufgabe vor, daß Register r18 auf 0 gesetzt werden soll, wenn im Register r17 der Zahlenwert 25 enthalten ist, dann lautet der Code&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    cpi     r17, 25         ; vergleiche r17 mit der Konstante 25&lt;br /&gt;
    brne    nicht_gleich    ; wenn nicht gleich, dann mach bei nicht_gleich weiter&lt;br /&gt;
    ldi     r18, 0          ; hier stehen nun Anweisungen für den Fall&lt;br /&gt;
                            ; dass R17 gleich 25 ist&lt;br /&gt;
    rjmp    weiter          ; meist will man den anderen Zeig nicht durchlaufen, darum der Sprung&lt;br /&gt;
nicht_gleich:&lt;br /&gt;
    ldi     r18,123         ; hier stehen nun Anweisungen für den Fall&lt;br /&gt;
                            ; dass R17 ungleich 25 ist&lt;br /&gt;
weiter:                     ; hier geht das Programm weiter&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ähnlicher Weise können die anderen bedingten Sprungbefehle eingesetzt werden, um die üblicherweise vorkommenden Vergleiche auf Gleichheit, Ungleichheit, Größer, Kleiner zu realisieren.&lt;br /&gt;
&lt;br /&gt;
===Schleifenkonstrukte===&lt;br /&gt;
&lt;br /&gt;
Ein immer wiederkehrendes Muster in der Programmierung ist eine &#039;&#039;&#039;Schleife&#039;&#039;&#039;. Die einfachste Form einer Schleife ist die &#039;&#039;&#039;Zählschleife&#039;&#039;&#039;. Dabei wird ein Register von einem Startwert ausgehend eine gewisse Anzahl erhöht, bis ein Endwert erreicht wird.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 10         ; der Startwert sei in diesem Beispiel 10&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    inc     r17             ; erhöhe das Zaehlregister&lt;br /&gt;
    cpi     r17, 134        ; mit dem Endwert vergleichen&lt;br /&gt;
    brne    loop            ; und wenn der Endwert noch nicht erricht ist&lt;br /&gt;
                            ; wird bei der Marke loop ein weiterer Schleifendurchlauf ausgeführt&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sehr oft ist es auch möglich das Konstrukt umzudrehen. Anstatt von einem Startwert aus zu inkrementieren genügt es die Anzahl der gewünschten Schleifendurchläufe in ein Register zu laden und dieses Register zu dekrementieren. Dabei kann man von der Eigenschaft der Dekrementieranweisung gebrauch machen, das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; zu beeinflussen. Ist das Ergebnis des Dekrements 0, so wird das &#039;&#039;&#039;Zero Flag (Z)&#039;&#039;&#039; gesetzt, welches wiederum in der nachfolgenden &#039;&#039;&#039;BRNE&#039;&#039;&#039; Anweisung für einen bedingen Sprung benutzt werden kann. Das vereinfacht die Schleife und spart eine Anweisung sowie einen Takt Ausführungzeit.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
    ldi     r17, 124        ; Die Anzahl der Wiederholungen in ein Register laden&lt;br /&gt;
loop:&lt;br /&gt;
                            ; an dieser Stelle stehen die Befehle, welche innerhalb der Schleife&lt;br /&gt;
                            ; mehrfach ausgeführt werden sollen&lt;br /&gt;
&lt;br /&gt;
    dec     r17             ; Schleifenzähler um 1 verringern, dabei wird das Zero Flag beeinflusst&lt;br /&gt;
    brne    loop            ; wenn r17 noch nicht 0 geworden ist -&amp;gt; Schleife wiederholen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=Interrupts|&lt;br /&gt;
zurücklink=AVR-Tutorial: Interrupts|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Mehrfachverzweigungen|&lt;br /&gt;
vorlink=AVR-Tutorial: Mehrfachverzweigung}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Temperatursensor&amp;diff=25211</id>
		<title>Temperatursensor</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Temperatursensor&amp;diff=25211"/>
		<updated>2007-12-25T17:33:26Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Formatierung überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Will man mit einem [[Mikrocontroller]] Temperaturen messen, dann braucht man&lt;br /&gt;
* einen [[Sensor]], der die Temperatur z.B. in eine Spannung oder einen Strom umwandelt&lt;br /&gt;
* einen [[ADC | AD-Wandler]], der das Signal digitalisiert. Der kann auf dem Sensor oder dem Mikrocontroller integriert sein.&lt;br /&gt;
&lt;br /&gt;
Temperatursensoren gibt es nun in allen möglichen Varianten. Vom temperaturabhängigen [[Widerstand]] bis zum fertig abgeglichenen All-in-one-Bauteil mit digitalem Ausgang. Wie bei allen Sensoren sollte man auch hier genau hinschauen und [[Auflösung und Genauigkeit]] unterscheiden.&lt;br /&gt;
&lt;br /&gt;
== Analoge Temperatursensoren ==&lt;br /&gt;
&lt;br /&gt;
=== PT100 ===&lt;br /&gt;
&lt;br /&gt;
Unter einem PT100 versteht man einen Platinwiderstand, der bei 0°C einen Widerstand von 100 Ohm hat.&lt;br /&gt;
Platinwiderstände sind temperaturabhängige Widerstände mit hoher Wiederholgenauigkeit und Konstanz.  Wegen der relativ geringen Widerstandsänderung von nur ca. 0,4 Ohm pro Grad ist etwas mehr Schaltungsaufwand erforderlich als bei anderen Sensoren. Genauere Formeln zur Temperaturbestimmung gibt es u.a. bei der [http://de.wikipedia.org/wiki/Pt100 Wikipedia]. Einen Schaltplan findet sich bei der [http://www.heise.de/ct/04/22/236/ c&#039;t].&lt;br /&gt;
&lt;br /&gt;
Die Sensoren gibt es auch mit anderen Widerstandswerten, z.B. mit 1000&amp;amp;Omega; und heißen dann entsprechend PT1000.&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* genormt&lt;br /&gt;
* hohe Linearität&lt;br /&gt;
* hohe Wiederholgenauigkeit&lt;br /&gt;
* einfach austauschbar&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* relativ teuer (bei segor.de ab 3,80&amp;amp;euro;)&lt;br /&gt;
* brauchen aufwendigere Auswerteschaltung&lt;br /&gt;
&lt;br /&gt;
=== NTC/PTC ===&lt;br /&gt;
&lt;br /&gt;
NTC und PTC sind temperaturabhängige Widerstände.&lt;br /&gt;
&lt;br /&gt;
* NTC (engl. &#039;&#039;&#039;N&#039;&#039;&#039;egative &#039;&#039;&#039;T&#039;&#039;&#039;emperature &#039;&#039;&#039;C&#039;&#039;&#039;oefficient, Heißleiter), hat bei hohen Temperaturen seinen niedrigsten Widerstand, z.B. Silizium&lt;br /&gt;
* PTC (engl. &#039;&#039;&#039;P&#039;&#039;&#039;ositive &#039;&#039;&#039;T&#039;&#039;&#039;emperature &#039;&#039;&#039;C&#039;&#039;&#039;oefficient, Kaltleiter), hat bei niedrigen Temperaturen seinen geringsten Widerstand, z.B. Glühlampe&lt;br /&gt;
&lt;br /&gt;
Um den Widerstandswert zu messen schaltet man sie mit einem normalen Widerstand oder einer [[Konstantstromquelle]] in Reihe zu einem [[Spannungsteiler]] und misst den Spannungsabfall. Eine Beispielschaltung findet sich [http://www.mathar.com/msp_thermo1.html hier].&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* billig (Reichelt 0,29 &amp;amp;euro;)&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* müssen abgeglichen werden&lt;br /&gt;
* brauchen A/D-Wandler&lt;br /&gt;
* sind nichtlinear&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://www.sprut.de/electronic/temeratur/temp.htm Temperaturabhängige Stromquelle und NTC/PTC inclusive Linearisierung]&lt;br /&gt;
&lt;br /&gt;
=== LMx35 ===&lt;br /&gt;
&lt;br /&gt;
Eine IC-Familie die pro Kelvin Temperaturänderung seine Ausgangsspannung um 10mV ändert. Die ICs gibt es in verschiedenen Genauigkeiten und Temperaturbereichen mit den Bezeichnungen LM135(A), LM235(A) und LM335(A). Der günstigste ist der LM335 mit einem Temperaturbereich von -40 ... +100°C.&lt;br /&gt;
In verschiedenen Bauformen erhältlich. Beispielschaltungen finden sich im [http://www.national.com/ds.cgi/LM/LM135.pdf Datenblatt] und [http://www.suessbrich.info/elek/elektherm1.html hier]&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* hat auch ohne Kalibrierung eine Genauigkeit von einem Grad (bei 25°C)&lt;br /&gt;
* relativ billig (LM335 bei Reichelt ab 0,87 &amp;amp;euro;)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* benötigt A/D-Wandler&lt;br /&gt;
* bei längerer Anschlussleitung störanfällig&lt;br /&gt;
&lt;br /&gt;
=== LM334 ===&lt;br /&gt;
&lt;br /&gt;
Ein IC ähnlich dem LM335 mit dem Unterschied, dass der durch das IC fließende Strom proportional von der Temperatur abhängt. Mit einer einfachen Schaltung aus nur zwei Widerständen kann man dann den Strom in einer Weise wandeln, dass pro Kelvin eine Sapnnungsänderung von 10mV ausgegeben wird. Da die Strom-Spannungswandlung auf der Platine (und damit nahe am AD-Wandler) stattfindet und die Übertragung des Messwerts durch einen Strom stattfindet, sind Störungen durch Netzbrumm etc. viel geringer als beim LM335&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* hat auch ohne Kalibrierung eine Genauigkeit von einem Grad (bei 25°C)&lt;br /&gt;
* relativ billig (Reichelt 0,90 &amp;amp;euro;)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* benötigt A/D-Wandler&lt;br /&gt;
* Bereich 0°C-70°C&lt;br /&gt;
&lt;br /&gt;
Ähnliche ICs:&lt;br /&gt;
* AD592 (Ausgangsstrom 1µA pro Kelvin, absolute Temperatur) Conrad 174912 8,50&amp;amp;euro;&lt;br /&gt;
&lt;br /&gt;
=== SMT160-30 ===&lt;br /&gt;
&lt;br /&gt;
Ist ein Zwischending zwischen Digital und Analog. Sein Ausgangssignal ist ein digitales PWM-Signal, zu dessen Messung man am besten den Input-Capture-Eingang eines Mikrocontrollers verwendet. Man kann ihn also wie einen analogen Sensor nur indirekt auslesen, anstatt über einen AD-Wandler hier über einen Timer.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* Digitales PWM-Signal ist unempfindlich gegen Störeinflüsse&lt;br /&gt;
* gibt es in SO8, TO18, TO92 und &amp;lt;b&amp;gt;TO220&amp;lt;/b&amp;gt;, gut befestigbar, z.B am Kühlkörper&lt;br /&gt;
* linear&lt;br /&gt;
* kein Abgleich nötig&lt;br /&gt;
&lt;br /&gt;
Nachteile (viele):&lt;br /&gt;
* benötigt Timer&lt;br /&gt;
* jittert extrem, genaue Messungen nur über Mittelung / Filterung möglich&lt;br /&gt;
* nicht nur das PWM-Verhältnis, sondern auf die Frequenz ist temp-abhängig (1-4kHz)&lt;br /&gt;
* teuer (Farnell 10,90&amp;amp;euro; +16%, Conrad 9,xx&amp;amp;euro; , www.hy-line.de ??).&lt;br /&gt;
* TO92 Gehäuse ist günstiger, dafür weniger genau&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* http://www.hy-line.de/co/sensor-tec/hersteller/smartec/smt-160-30/index.html&lt;br /&gt;
&lt;br /&gt;
=== Thermoelement ===&lt;br /&gt;
&lt;br /&gt;
Ein Thermoelement besteht im einfachsten Fall aus zwei ungleichen Metallendrähten, die an einem Punkt miteinander verbunden sind und bei dem die Verbindungsstelle einer anderen Temperatur ausgesetzt ist als die offenen Enden der Drähte. An den offenen Enden der Drähten entsteht eine Spannung (Thermospannung). Dieser Effekt wurde 1821 von Thomas Seebeck entdeckt ([http://de.wikipedia.org/wiki/Seebeck-Effekt Seebeck-Effekt] bei Wikipedia). Eine weitere Anwendung ist der thermoelektrische Generator (&amp;quot;Thermogenerator&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Vorteil:&lt;br /&gt;
* über einen sehr weiten Temperaturbereich einsetzbar&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* die sehr geringen Temperaturspannungen im Mikrovoltbereich benötigen eine sehr gute Auswertelektronik (guter Analogteil + AD-Wandler).&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://digital.ni.com/worldwide/germany.nsf/web/all/7A4F02BAEFEC22AC802567F6003E0D6E  Temperaturmessung mit Thermoelementen] - Eine Einführung von David Potter (deutsche Überarbeitung: G.Sinkovic) (inkl. Erläuterung der Kaltstellenkompensation)&lt;br /&gt;
* [http://www.ipetronik.com/pdf/Newsletter/Ipetronik_NL2_2004_d.pdf Warum Thermoelemente Relativtemperaturen messen! oder Was ist eine Kaltstelle?] - Technische Information von www.ipetronik.com (PDF, 272 KB)&lt;br /&gt;
&lt;br /&gt;
== Digitale Temperatursensoren ==&lt;br /&gt;
&lt;br /&gt;
=== DS1621 ===&lt;br /&gt;
&lt;br /&gt;
Der DS1621 ist Temperatursensor und A/D-Wandler in einem. Er gibt seine Daten per [[I²C]]-[[Bus]] aus. Ein Schaltplan für einen elektronischen Thermometer mit diesem IC findet sich [http://www.myplace.nu/avr/thermo/ hier].&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* kein A/D-Wandler nötig&lt;br /&gt;
* da I²C ein Bus ist, kann man mehrere DS1621 und andere I²C-Bausteine zusammen anschließen und braucht dafür trotzdem nur zwei I/O-Ports.&lt;br /&gt;
* Genauigkeit +-0,5°&lt;br /&gt;
* Auflösung besser 0,01°, wenn man die beiden Zählerregister (Count-Remain und Count-per-C) auswertet&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* teuer (Segor 5,80&amp;amp;euro;; RS 3,95&amp;amp;euro;; Conrad 4,99&amp;amp;euro;)&lt;br /&gt;
* trotzdem die meisten Register [[Speicher#NVRAM | nichtflüchtig]] sind, kann man ihn nicht als Stand-Alone-Thermostat einsetzen, da er erst nach einem Start-Conversion-Befehl zu messen beginnt.&lt;br /&gt;
&lt;br /&gt;
Nachfolger:&lt;br /&gt;
* DS1631, DS1631A (Auto-Start-&amp;gt; Stand-Alone-Thermostat), DS1731&lt;br /&gt;
* weitere Stand-Alone-Thermostaten: DS1821, DS1629&lt;br /&gt;
&lt;br /&gt;
=== LM75 ===&lt;br /&gt;
&lt;br /&gt;
Der LM75 ist so ähnlich wie der DS1621, allerdings nur in SMD erhältlich und nicht so genau. Er ist aber öfters mal auf PC-Mainboards zu finden, so dass man beim Schlachten eines solchen günstig an einen Temperatursensor kommen kann. Einen Schaltplan findet man [http://www.mcselec.com/index.php?option=com_docman&amp;amp;task=cat_view&amp;amp;gid=83&amp;amp;limit=1&amp;amp;limitstart=35 hier].&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* kein A/D-Wandler nötig&lt;br /&gt;
* I²C-Bus Ausgang&lt;br /&gt;
* billiger als DS1621 (Reichelt 2,10 &amp;amp;euro;; RS 3V: 3,75&amp;amp;euro;; 5V: 2,72&amp;amp;euro;)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* nur im SMD-Gehäuse erhältlich&lt;br /&gt;
* relativ ungenau (+-2°), kann man jedoch kalibrieren / kompensieren&lt;br /&gt;
&lt;br /&gt;
Kompatible Typen:&lt;br /&gt;
* AD7415ART&lt;br /&gt;
&lt;br /&gt;
=== DS18S20 / DS18B20 ===&lt;br /&gt;
&lt;br /&gt;
Der DS18S20 (Nachfolger des DS1820) und DS18B20 sind scheinbar Temperatursensoren und A/D-Wandler in einem. Wenn man genauer hinschaut, stellt man fest, dass es sich um direktwandelnde Sensoren handelt. Die Temperatur wird ohne Umweg über eine analoge Zwischengröße (Spannung oder Strom) in ein digitales Signal überführt. Die Datenkommunikation erfolgt über ein 1-Wire-Interface, wodurch man am [[Mikrocontroller]] mit nur einen einzigen I/O-Pin auskommen kann. Außerdem beherrschen sie die parasitäre Stromversorgung, d.h. man braucht für Daten und Stromversorgung zusammen nur zwei Leitungen.  Der DS18B20 hat 12 Bit Auflösung gegenüber 9 Bit Auflösung beim DS18S20.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* bereits kalibriert&lt;br /&gt;
* Genauigkeit +-0,5°&lt;br /&gt;
* 1-Wire-Ausgang&lt;br /&gt;
&lt;br /&gt;
Nachteil:&lt;br /&gt;
* ziemlich teuer (Reichelt 4,50&amp;amp;euro;)&lt;br /&gt;
* bei http://shop.medhost.at/ für 3,24&amp;amp;euro; (2,70&amp;amp;euro;+20%UST) + Versand EU 6&amp;amp;euro;&lt;br /&gt;
&lt;br /&gt;
Links:&lt;br /&gt;
* [http://chaokhun.kmitl.ac.th/~kswichit/avrthermo/avrthermo.html Ein Schaltplan]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-27035.html Code zur Ansteuerung]&lt;br /&gt;
* [http://pdfserv.maxim-ic.com/en/ds/DS18S20.pdf Datenblatt DS18S20] &lt;br /&gt;
* [http://pdfserv.maxim-ic.com/en/ds/DS18B20.pdf Datenblatt DS18B20]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-248219.html Webserver zur Ansteuerung von bis zu 63 Bausteinen]&lt;br /&gt;
&lt;br /&gt;
=== DS1921 / DS1922 ===&lt;br /&gt;
&lt;br /&gt;
Sind wie die DS1821 1-wire-Sensoren mit zusätzlicher Logging-Funktion.&lt;br /&gt;
Im iButton-Gehäuse befindet sich eine Lithium-Zelle, eine RTC, CMOS-RAM und der Temp-Sensor. Nach umfangreicher Progammierung startet der Button seine Mission (Aufzeichnung des Temperaturverlaufs).&lt;br /&gt;
Gibt es auch mit zusätzlicher Feuchtemessung (DS1923).&lt;br /&gt;
&lt;br /&gt;
=== SHT1x/SHT7x ===&lt;br /&gt;
&lt;br /&gt;
Der SHT1x/SHT7x (SHT10, SHT11, SHT15, STH71, SHT75) sind kombinierte Temperatur- und Feuchtesensoren von [http://www.sensirion.com Sensirion]. Sie unterscheiden sich in Bauform und Genauigkeit.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
* digitale Schnittstelle mit einfacher [[I²C]]-&#039;&#039;ähnlicher&#039;&#039; Ansteuerung&lt;br /&gt;
* keine Kalibrierung notwendig&lt;br /&gt;
* Beispielcode (C, MC51) auf der Sensirion-Seite verfügbar (relativ leicht portierbar)&lt;br /&gt;
* interne Heizelemente (Funktionsprüfung, &amp;quot;rauhe&amp;quot; Umgebung)&lt;br /&gt;
* Spannungsmonitor (&amp;quot;Battery fail&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
* kann nicht am [[I²C]] Bus betrieben werden, theoretisch gleiche Clockleitung möglich, fixe Adresse&lt;br /&gt;
* relativ teuer (Farnell 18,60&amp;amp;euro;)&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=25204</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=25204"/>
		<updated>2007-12-24T22:07:38Z</updated>

		<summary type="html">&lt;p&gt;FalkB: &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;
&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt konstante 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;avrasm&amp;gt;&lt;br /&gt;
    ldi     r16, 0xA0           ; Schreibt den Wert 0xA0 in das Register r16 &lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;ldi&amp;lt;/b&amp;gt; steht hierbei für &amp;lt;b&amp;gt;l&amp;lt;/b&amp;gt;oa&amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;i&amp;lt;/b&amp;gt;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;
&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;avrasm&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;
&amp;lt;/avrasm&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 &amp;lt;i&amp;gt;variable&amp;lt;/i&amp;gt; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&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 &amp;lt;b&amp;gt;sts&amp;lt;/b&amp;gt;-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;
&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 &amp;lt;i&amp;gt;zahlen_start&amp;lt;/i&amp;gt;, 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 &amp;lt;b&amp;gt;indirekte Adressierung&amp;lt;/b&amp;gt;, 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. Da diese Register auf Daten zeigen, nennt man sie logischerweise Zeigerregister (engl. Pointer). Sie tragen die Namen &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;Y&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;, 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 &amp;lt;b&amp;gt;XL&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;XH&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;YL&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;YH&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; angesprochen werden können. &amp;lt;b&amp;gt;L&amp;lt;/b&amp;gt;(low) und &amp;lt;b&amp;gt;H&amp;lt;/b&amp;gt;(high) bedeutet hierbei dass die unteren respektive die oberen 8 Bits der 16-Bit-Adresse gemeint ist.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Zeigerregister des AVR&#039;&#039;&#039;&lt;br /&gt;
! Register || alternativer Name || 16 Bit Zeigerregister&lt;br /&gt;
|-&lt;br /&gt;
| r26 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| XL &lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| X&lt;br /&gt;
|-&lt;br /&gt;
| r27 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| XH&lt;br /&gt;
|-&lt;br /&gt;
| r28 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| YL&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| Y&lt;br /&gt;
|-&lt;br /&gt;
| r29 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| YH&lt;br /&gt;
|-&lt;br /&gt;
| r30 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| ZL&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| Z&lt;br /&gt;
|-&lt;br /&gt;
| r31 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| ZH&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register 16 Bit breit ist, müssen wir &amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt; in zwei einzelnen &amp;lt;b&amp;gt;ldi&amp;lt;/b&amp;gt; Operationen beschreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit &amp;lt;b&amp;gt;LOW(...)&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;HIGH(...)&amp;lt;/b&amp;gt; bekommt man die unteren respektive die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir gerade die unteren/oberen 8 Bit der Adresse in die Register &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt;/&amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; schreiben wollen.&lt;br /&gt;
&lt;br /&gt;
Dann können wir mit dem &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;-Befehl die Zahl von der Speicherstelle lesen, auf die das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register verweist. Wir schreiben den Wert in das Register r17. Zum Aufsummieren wollen wir das Register r18 verwenden, welches ganz zu Anfang mit &amp;lt;b&amp;gt;clr&amp;lt;/b&amp;gt; auf 0 gesetzt wird.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20              ; 20 Byte reservieren, das soll die Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
; Irgendwo vorher werden die Zahlen geschrieben, das interessiert&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;
    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 das Ergebnis.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Das Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: Das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register muss jedes manuell inkrementiert werden, 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;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
können durch folgende Zeile ersetzt werden:&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
Das spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &amp;lt;b&amp;gt;nach&amp;lt;/b&amp;gt; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &amp;lt;b&amp;gt;vor&amp;lt;/b&amp;gt; der Ausführung des Befehls dekrementiert. Das Predekrement eignet sich, um &amp;lt;i&amp;gt;rückwärts&amp;lt;/i&amp;gt; durch linear angeordnete Datenzu gehen.&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
,&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=25203</id>
		<title>Adressierung</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Adressierung&amp;diff=25203"/>
		<updated>2007-12-24T22:06:08Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Kosmetik&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;
&lt;br /&gt;
Eigentlich keine Adressierungsart, aber dennoch sehr wichtig, ist die Möglichkeit direkt konstante 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;avrasm&amp;gt;&lt;br /&gt;
    ldi     r16, 0xA0           ; Schreibt den Wert 0xA0 in das Register r16 &lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;ldi&amp;lt;/b&amp;gt; steht hierbei für &amp;lt;b&amp;gt;l&amp;lt;/b&amp;gt;oa&amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;i&amp;lt;/b&amp;gt;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;
&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;avrasm&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;
&amp;lt;/avrasm&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 &amp;lt;i&amp;gt;variable&amp;lt;/i&amp;gt; in obigem Beispiel eine Adresse und somit nur eine Zahl darstellt, kann man zur Entwicklungszeit auch Konstanten addieren:&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&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 &amp;lt;b&amp;gt;sts&amp;lt;/b&amp;gt;-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;
&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 &amp;lt;i&amp;gt;zahlen_start&amp;lt;/i&amp;gt;, 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 &amp;lt;b&amp;gt;indirekte Adressierung&amp;lt;/b&amp;gt;, 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. Da diese Register auf Daten zeigen, nennt man sie logischerweise Zeigerregister (engl. Pointer). Sie tragen die Namen &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;Y&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;, 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 &amp;lt;b&amp;gt;XL&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;XH&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;YL&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;YH&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; angesprochen werden können. &amp;lt;b&amp;gt;L&amp;lt;/b&amp;gt;(low) und &amp;lt;b&amp;gt;H&amp;lt;/b&amp;gt;(high) bedeutet hierbei dass die unteren respektive die oberen 8 Bits der 16-Bit-Adresse gemeint ist.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+ &#039;&#039;&#039;Zeigerregister des AVR&#039;&#039;&#039;&lt;br /&gt;
! Register || alternativer Name || 16 Bit Zeigerregister&lt;br /&gt;
|-&lt;br /&gt;
| r26 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| XL &lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| X&lt;br /&gt;
|-&lt;br /&gt;
| r27 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| XH&lt;br /&gt;
|-&lt;br /&gt;
| r28 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| YL&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| Y&lt;br /&gt;
|-&lt;br /&gt;
| r29 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| YH&lt;br /&gt;
|-&lt;br /&gt;
| r30 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| ZL&lt;br /&gt;
| align=&amp;quot;center&amp;quot; rowspan=&amp;quot;2&amp;quot;| Z&lt;br /&gt;
|-&lt;br /&gt;
| r31 &lt;br /&gt;
| align=&amp;quot;center&amp;quot;| ZH&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wir werden beispielhalber das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register für unser Problem verwenden. Dazu müssen wir zunächst die Adresse der ersten Zahl in dieses laden. Da das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register 16 Bit breit ist, müssen wir &amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt; in zwei einzelnen &amp;lt;b&amp;gt;ldi&amp;lt;/b&amp;gt; Operationen beschreiben. Der AVR-Assembler bietet uns hier zwei praktische Funktionen: Mit &amp;lt;b&amp;gt;LOW(...)&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;HIGH(...)&amp;lt;/b&amp;gt; bekommt man die unteren respektive die oberen 8 Bit einer Speicheradresse. Das kommt uns gerade recht, da wir gerade die unteren/oberen 8 Bit der Adresse in die Register &amp;lt;b&amp;gt;ZL&amp;lt;/b&amp;gt;/&amp;lt;b&amp;gt;ZH&amp;lt;/b&amp;gt; schreiben wollen.&lt;br /&gt;
&lt;br /&gt;
Dann können wir mit dem &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;-Befehl die Zahl von der Speicherstelle lesen, auf die das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register verweist. Wir schreiben den Wert in das Register r17. Zum Aufsummieren wollen wir das Register r18 verwenden, welches ganz zu Anfang mit &amp;lt;b&amp;gt;clr&amp;lt;/b&amp;gt; auf 0 gesetzt wird.&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.dseg&lt;br /&gt;
zahlen_start: .byte 20   			; 20 Byte reservieren, das soll die Maximalanzahl sein&lt;br /&gt;
&lt;br /&gt;
.cseg&lt;br /&gt;
; Irgendwo vorher werden die Zahlen geschrieben, das interessiert&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;
    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 das Ergebnis.&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Das Programm funktioniert zwar schon, aber eine Sache ist unpraktisch: Das &amp;lt;b&amp;gt;Z&amp;lt;/b&amp;gt;-Register muss jedes manuell inkrementiert werden, 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;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
können durch folgende Zeile ersetzt werden:&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
Das spart Ausführungszeit und macht den Code kürzer. Zu beachten ist, dass die Inkrementierung erst &amp;lt;b&amp;gt;nach&amp;lt;/b&amp;gt; der Ausführung des eigentlichen Befehls durchgeführt wird.&lt;br /&gt;
&lt;br /&gt;
===Predekrement===&lt;br /&gt;
Äquivalent zum Postinkrement gibt es auch die Möglichkeit des Dekrementierens. Hierbei wird der Wert jedoch &amp;lt;b&amp;gt;vor&amp;lt;/b&amp;gt; der Ausführung des Befehls dekrementiert. Das Predekrement eignet sich, um &amp;lt;i&amp;gt;rückwärts&amp;lt;/i&amp;gt; durch linear angeordnete Datenzu gehen.&lt;br /&gt;
&amp;lt;avrasm&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;/avrasm&amp;gt;&lt;br /&gt;
,&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=25202</id>
		<title>HD44780</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=HD44780&amp;diff=25202"/>
		<updated>2007-12-24T21:34:40Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Kosmetik&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Der HD44780 ist ein Steuer-IC für Textdisplays (siehe [[LCD]]). Praktisch alle Textdisplays werden mit diesem oder einem kompatiblen Controller (z.B. KS0066) angesteuert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Pinbelegung ===&lt;br /&gt;
&lt;br /&gt;
Die Pinbelegung des ICs selber ist für den Anwender praktisch uninteressant. Hier ist die Pinbelegung vieler damit ausgestatteten LCD-Module angegeben.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
!Pin || Funktion || Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|1 || | VSS || Versorgungsspannung GND&lt;br /&gt;
|-&lt;br /&gt;
|2 || | VCC || Versorgungsspannung, meistens 5V&lt;br /&gt;
|-&lt;br /&gt;
|3 || | V0 ||Kontrastspannung, zwischen VSS und VCC, kann auf VSS gelegt oder via Poti angeschlossen werden.&amp;lt;BR&amp;gt;Bei großflächigen LCDs oder LCDs für den erweiterten Temperaturbereich kann auch eine negative Kontrastspannung nötig sein. &lt;br /&gt;
|-&lt;br /&gt;
|4 || | RS || Registerauswahl&amp;lt;BR&amp;gt;0 = Befehlsregister, 1 = Datenregister &lt;br /&gt;
|-&lt;br /&gt;
|5 || | R/W || Lese oder Schreibzugriff, kann meistens fest auf GND gezogen werden&amp;lt;BR&amp;gt;0 = Schreiben, 1 = Lesen, &lt;br /&gt;
|-&lt;br /&gt;
|6 || | E || Taktleitung&lt;br /&gt;
|-&lt;br /&gt;
|7 || | DB0 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|8 || |DB1 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|9 || |DB2 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|10 || |DB3 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|11 || |DB4 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|12 || |DB5 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|13 || |DB6 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|14 || |DB7 || Datenleitung&lt;br /&gt;
|-&lt;br /&gt;
|15 || A || Anode der LED-Hintergrundbeleuchtung&lt;br /&gt;
|-&lt;br /&gt;
|16 || K || Kathode der LED-Hintergrundbeleuchtung&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Speicher===&lt;br /&gt;
&lt;br /&gt;
Ein HD44780 besitzt mehrere Speicher. In ihnen wird der Inhalt des Displays sowie das Aussehen von Sonderzeichen gespeichert.&lt;br /&gt;
&lt;br /&gt;
==== CGROM  ====&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;ROM&#039;&#039;&#039; enthält die Zeichen in Form von 5x8 oder 5x10 Punktmatrizen. Er kann nicht geändert werden, ausser man sendet einen EEPROM zu Hitachi für eine Massenproduktion. Es sind viele verschiedene ROMs (Zeichensätze, engl. Fonts) verfügbar. Er ist für den Anwender nicht zugänglich.&lt;br /&gt;
&lt;br /&gt;
==== CGRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;C&#039;&#039;&#039;haracter &#039;&#039;&#039;G&#039;&#039;&#039;enerator &#039;&#039;&#039;RAM&#039;&#039;&#039; können acht 5x8 Pixel oder vier 5x10 Pixel grosse benutzerdefinierte Zeichen abgelegt werden. Wenn keine benutzerdefinierte Zeichen gebraucht werden kann dieser Bereich als Auslagerungsspeicher für den ansteuernden Mikrocontroller benutzt werden.&lt;br /&gt;
&lt;br /&gt;
==== DDRAM ====&lt;br /&gt;
&lt;br /&gt;
Im &#039;&#039;&#039;D&#039;&#039;&#039;isplay &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;RAM&#039;&#039;&#039; ist der Inhalt des LCDs gespeichert. Die Kodierung orientiert sich weitestgehend am ASCII Zeichensatz.&lt;br /&gt;
&lt;br /&gt;
=== Kommandos ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+HD44780 Befehlssatz&lt;br /&gt;
!Befehl&lt;br /&gt;
!RS&lt;br /&gt;
!RW&lt;br /&gt;
!D7&lt;br /&gt;
!D6&lt;br /&gt;
!D5&lt;br /&gt;
!D4&lt;br /&gt;
!D3&lt;br /&gt;
!D2&lt;br /&gt;
!D1&lt;br /&gt;
!D0&lt;br /&gt;
|-&lt;br /&gt;
|Bildschirminhalt löschen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|Cursor auf Startpos&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Modus festlegen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|I/D&lt;br /&gt;
|S&lt;br /&gt;
|-&lt;br /&gt;
|Display/Cursor &lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|D&lt;br /&gt;
|C&lt;br /&gt;
|B&lt;br /&gt;
|-&lt;br /&gt;
|Cursor/Display schieben&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|S/C&lt;br /&gt;
|R/L&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|Funktionen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|DL&lt;br /&gt;
|N&lt;br /&gt;
|F&lt;br /&gt;
|X&lt;br /&gt;
|X&lt;br /&gt;
|-&lt;br /&gt;
|CGRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan =&amp;quot;6&amp;quot; align=&amp;quot;center&amp;quot;|CGRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|DDRAM Adresse setzen&lt;br /&gt;
|0&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Adresse/Status lesen&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|BF&lt;br /&gt;
|colspan=&amp;quot;7&amp;quot; align=&amp;quot;center&amp;quot;|CG-/DDRAM-Adresse&lt;br /&gt;
|-&lt;br /&gt;
|Daten in DDRAM/CGRAM schreiben&lt;br /&gt;
|1&lt;br /&gt;
|0&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|-&lt;br /&gt;
|Daten aus DDRAM/CGRAM lesen&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|colspan=&amp;quot;8&amp;quot; align=&amp;quot;center&amp;quot;|Daten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|+Kodierung der Steuerbits&lt;br /&gt;
!Bit&lt;br /&gt;
!0&lt;br /&gt;
!1&lt;br /&gt;
|-&lt;br /&gt;
|I/D  &lt;br /&gt;
|Cursorposition dekrementieren &lt;br /&gt;
|Cursorposition inkrementieren  &lt;br /&gt;
|-&lt;br /&gt;
|S&lt;br /&gt;
|Displayinhalt fest&lt;br /&gt;
|Displayinhalt weiterschieben&lt;br /&gt;
|-&lt;br /&gt;
|D&lt;br /&gt;
|Display aus&lt;br /&gt;
|Display an&lt;br /&gt;
|-&lt;br /&gt;
|C&lt;br /&gt;
|Cursor aus&lt;br /&gt;
|Cursor an&lt;br /&gt;
|-&lt;br /&gt;
|B&lt;br /&gt;
|Cursor blinkt nicht&lt;br /&gt;
|Cursor blinkt&lt;br /&gt;
|-&lt;br /&gt;
|S/C&lt;br /&gt;
|Cursor bewegen&lt;br /&gt;
|Displayinhalt schieben&lt;br /&gt;
|-&lt;br /&gt;
|R/L&lt;br /&gt;
|Nach links schieben&lt;br /&gt;
|Nach rechts schieben&lt;br /&gt;
|-&lt;br /&gt;
|DL&lt;br /&gt;
|4-Bit Interface&lt;br /&gt;
|8-Bit Interface&lt;br /&gt;
|-&lt;br /&gt;
|N&lt;br /&gt;
|1-zeiliges Display&lt;br /&gt;
|2/4-zeiliges Display&lt;br /&gt;
|-&lt;br /&gt;
|F&lt;br /&gt;
|5x7-Font&lt;br /&gt;
|5x10-Font&lt;br /&gt;
|-&lt;br /&gt;
|BF&lt;br /&gt;
|Kann Kommandos annehmen&lt;br /&gt;
|Ist beschäftigt &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ansteuerung per Microcontroller ==&lt;br /&gt;
&lt;br /&gt;
Die Ansteuerung eines HD44780-basierten Displays gestaltet sich sowohl in Bezug auf den Hardware- als auch den Softwareaufwand recht einfach. Sie eignet sich somit sehr gut als Übungsprojekt für Anfänger.&lt;br /&gt;
&lt;br /&gt;
Das HD44780-Interface besteht aus acht Datenleitungen (D0-D7) sowie den drei Steuerleitungen RS (&#039;&#039;&#039;R&#039;&#039;&#039;egister &#039;&#039;&#039;S&#039;&#039;&#039;elect), R/W (&#039;&#039;&#039;R&#039;&#039;&#039;ead/&#039;&#039;&#039;W&#039;&#039;&#039;rite) und E (&#039;&#039;&#039;E&#039;&#039;&#039;nable). Die Displays werden mit 5V Betriebsspannung versorgt, die Kontrastspannung V0 bekommt man, indem man ein 10k&amp;amp;Omega;-[[Potentiometer]] zwischen VCC und GND anschließt und den mittleren Anschluss als Kontrastspannung verwendet. Bei ca. 0,5 Volt werden dann die Pixel sichtbar. Optional haben viele Displays noch eine LED-Beleuchtung eingebaut, diese kann manchmal direkt an 5V angeschlossen werden, da der Vorwiderstand auf der LCD-Platine integriert ist. Oft ist aber auch ein externer Vorwiderstand nötig! Darum vorher besser im Datenblatt nachschauen. Möchte man IO-Pins am Mikrocontroller sparen, muss das Display im 4-Bit-Modus betrieben werden.&lt;br /&gt;
&lt;br /&gt;
Beispielprogramme zur Ansteuerung findet man zuhauf im Internet, u.a. auch hier: [[AVR-Tutorial: LCD]]&lt;br /&gt;
&lt;br /&gt;
=== Fertige Projekte oder Bibliotheken ===&lt;br /&gt;
&lt;br /&gt;
* Peter Fleurys [http://homepage.hispeed.ch/peterfleury/avr-software.html LCD library for HD44870 based LCDs]&lt;br /&gt;
* Kai Klenovseks [http://kk.elektronik-4u.de/index.php?Sid=9 LCD library for HD44870 based LCDs]&lt;br /&gt;
* avr-libc [http://www.nongnu.org/avr-libc/user-manual/group__stdiodemo.html Demo-Projekt für stdio]&lt;br /&gt;
* [http://wwwiti.cs.uni-magdeburg.de/~buchmann/privat/lcd.htm Grundlagen und Code für 8051] von Erik Buchmann&lt;br /&gt;
* [http://home.iae.nl/users/pouweha/lcd/lcd.shtml &#039;&#039;How to control a HD44780-based Character-LCD&#039;&#039;] von Peter Ouwehand. Beispiele für 8051, PIC16C54, AT90S2313&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=25197</id>
		<title>AVR-Tutorial: Speicher</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=AVR-Tutorial:_Speicher&amp;diff=25197"/>
		<updated>2007-12-24T13:04:02Z</updated>

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

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

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

		<summary type="html">&lt;p&gt;FalkB: /* Schreiben */ EEPROM Schreibzugriff korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Speichertypen ==&lt;br /&gt;
&lt;br /&gt;
Die AVR-Mikrocontroller besitzen 3 verschiedene Arten von Speichern: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Flash&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;EEPROM&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;RAM&amp;lt;/th&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Schreibzyklen&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;&amp;amp;gt;10.000&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;&amp;amp;gt;100.000&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Lesezyklen&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;unbegrenzt&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;flüchtig&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;nein&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;nein&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;ja&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim AT90S2333&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;2 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;128 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;128 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim AT90S4433&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;4 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;256 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;128 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&lt;br /&gt;
  &amp;lt;th align=&amp;quot;left&amp;quot;&amp;gt;Größe beim ATmega8&amp;lt;/th&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;8 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;512 Byte&amp;lt;/td&amp;gt;&lt;br /&gt;
  &amp;lt;td&amp;gt;1 KB&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Flash-ROM ===&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Flash-ROM&#039;&#039;&#039; der AVRs dient als Programmspeicher. Über den Programmieradapter werden die kompilierten Programme vom PC an den Controller übertragen und im Flash-ROM abgelegt. Bei der Programmausführung wird das ROM Byte für Byte ausgelesen und ausgeführt, es lässt sich aber auch zur Speicherung von Daten (z.B. Texte für eine LCD-Anzeige) nutzen. Vom laufenden Programm aus kann man das ROM normalerweise nur lesen, nicht beschreiben.&lt;br /&gt;
&lt;br /&gt;
Es kann beliebig oft ausgelesen werden, aber (theoretisch) nur ~10.000 mal beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
=== EEPROM ===&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;EEPROM&#039;&#039;&#039; ist wie das Flash ein nichtflüchtiger Speicher, die Daten bleiben also auch nach dem Ausschalten der Betriebsspannung erhalten. Es kann beliebig oft gelesen und mindestens 100.000 mal beschrieben werden. Bei den AVRs kann man es z.B. als Speicher für Messwerte oder Einstellungen benutzen. &lt;br /&gt;
&lt;br /&gt;
=== [[RAM]] ===&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;RAM&#039;&#039;&#039; ist ein flüchtiger Speicher, d.h. die Daten gehen nach dem Ausschalten verloren. Es kann beliebig oft gelesen und beschrieben werden, weshalb es sich zur Speicherung von Variablen eignet für die die Register R0-R31 nicht ausreichen (siehe Kapitel [[AVR-Tutorial: SRAM|SRAM]]). Daneben dient es als Speicherort für den Stack, in dem z.B. bei Unterprogrammaufrufen (rcall) die Rücksprungadresse gespeichert wird (siehe Kapitel 3).&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Flash-ROM ===&lt;br /&gt;
&lt;br /&gt;
Die erste und wichtigste Anwendung des Flash-ROMs kennen wir bereits: Das Speichern von Programmen, die wir nach dem Assemblieren dort hineingeladen haben. Nun sollen aber auch vom laufenden Programm aus Daten ausgelesen werden. &lt;br /&gt;
&lt;br /&gt;
Um die Daten wieder auszulesen, muss man die Adresse, auf die zugegriffen werden soll, in den &#039;&#039;&#039;Z-Pointer&#039;&#039;&#039; laden. Der Z-Pointer besteht aus den Registern &#039;&#039;&#039;R30&#039;&#039;&#039; (Low-Byte) und &#039;&#039;&#039;R31&#039;&#039;&#039; (High-Byte), daher kann man das Laden einer Konstante wie gewohnt mit dem Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; durchführen. Statt R30 und R31 kann man übrigens einfach &#039;&#039;&#039;ZL&#039;&#039;&#039; und &#039;&#039;&#039;ZH&#039;&#039;&#039; schreiben, da diese Synonyme bereits in der include-Datei m8def.inc definiert sind. &lt;br /&gt;
&lt;br /&gt;
Wenn die richtige Adresse erstmal im Z-Pointer steht, geht das eigentliche Laden der Daten ganz einfach mit dem Befehl &#039;&#039;&#039;lpm&#039;&#039;&#039;. Dieser Befehl, der im Gegensatz zu out, ldi usw. keine Operanden hat, veranlasst dass das durch den Z-Pointer addressierte Byte aus dem Programmspeicher in das Register &#039;&#039;&#039;R0&#039;&#039;&#039; geladen wird, von wo aus man es weiterverarbeiten kann. &lt;br /&gt;
&lt;br /&gt;
Jetzt muss man nur noch wissen, wie man dem Assembler überhaupt beibringt, dass er die von uns festgelegte Daten im ROM plazieren soll, und wie man dann an die Adresse kommt an der sich diese Daten befinden. Um den Programmspeicher mit Daten zu füllen, gibt es die [http://www.mikrocontroller.net/studiohelp/Assembler/directives.html Direktiven] .db und .dw. In der Regel benötigt man nur .db, was folgendermaßen funktioniert: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
daten:&lt;br /&gt;
           .db 12, 20, 255, 0xFF, 0b10010000&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Ausschnitt sagt dem Assembler, dass er die angegebenen Bytes nacheinander im Speicher platzieren soll; wenn man die Zeile also assembliert, erhält man eine Hex-Datei, die nur diese Daten enthält.&lt;br /&gt;
&lt;br /&gt;
Aber was soll das &#039;&#039;&#039;daten:&#039;&#039;&#039; am Anfang der Zeile? Bis jetzt haben wir Labels nur als Sprungmarken verwendet, um den Befehlen &#039;&#039;&#039;rcall&#039;&#039;&#039; und &#039;&#039;&#039;rjmp&#039;&#039;&#039; zu sagen, an welche Stelle im Programm gesprungen werden soll. Würden wir in diesem Fall &#039;&#039;&#039;rjmp daten&#039;&#039;&#039; im Programm stehen haben, dann würde die Programmausführung zur Stelle &#039;&#039;&#039;daten:&#039;&#039;&#039; springen, und versuchen die sinnlosen Daten als Befehle zu interpretieren - was mit Sicherheit dazu führt, dass der Controller Amok läuft. &lt;br /&gt;
&lt;br /&gt;
Statt nach &#039;&#039;&#039;daten:&#039;&#039;&#039; zu springen, sollten wir die Adresse besser in den Z-Pointer laden. Da der Z-Pointer aus zwei Bytes besteht, brauchen wir dazu zweimal den Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039;: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
           ldi ZL, LOW(daten*2)                      ; Low-Byte der Adresse in Z-Pointer&lt;br /&gt;
           ldi ZH, HIGH(daten*2)                     ; High-Byte der Adresse in Z-Pointer&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie man sieht, ist das Ganze sehr einfach: Man kann die Labels im Assembler direkt wie Konstanten verwenden. Über die Multiplikation der Adresse mit 2 sollte man sich erst mal keine Gedanken machen: &amp;quot;Das ist einfach so.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Um zu zeigen wie das alles konkret funktioniert, ist das folgende Beispiel nützlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt; &lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ldi R16, 0xFF&lt;br /&gt;
        out DDRB, R16                     ; Port B: Ausgang&lt;br /&gt;
&lt;br /&gt;
        ldi ZL, LOW(daten*2)              ; Low-Byte der Adresse in Z-Pointer&lt;br /&gt;
        ldi ZH, HIGH(daten*2)             ; High-Byte der Adresse in Z-Pointer&lt;br /&gt;
&lt;br /&gt;
        lpm                               ; durch Z-Pointer adressiertes Byte nach R0 lesen&lt;br /&gt;
        mov R16, R0                       ; nach R16 kopieren&lt;br /&gt;
        out PORTB, R16                    ; an PORTB ausgeben&lt;br /&gt;
&lt;br /&gt;
ende:   rjmp ende                         ; Endlosschleife&lt;br /&gt;
&lt;br /&gt;
daten:&lt;br /&gt;
        .db 0b10101010&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dieses Programm assembliert und in den Controller überträgt, dann kann man auf den an Port B angeschlossenen LEDs das mit &#039;&#039;&#039;.db 0b10101010&#039;&#039;&#039; im Programmspeicher abgelegte Bitmuster sehen. &lt;br /&gt;
&lt;br /&gt;
Eine häufige Anwendung von &#039;&#039;&#039;lpm&#039;&#039;&#039; ist das Auslesen von Zeichenketten (&amp;quot;Strings&amp;quot;) aus dem Flash-ROM und die Ausgabe an den seriellen Port oder ein LCD. Das folgende Programm gibt in einer Endlosschleife den Text &amp;quot;AVR-Assembler ist ganz einfach&amp;quot;, gefolgt von einem Zeilenumbruch, an den UART aus. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp = R16&lt;br /&gt;
&lt;br /&gt;
        ldi R16, RAMEND&lt;br /&gt;
        out SPL, R16                      ; Stackpointer initialisieren&lt;br /&gt;
&lt;br /&gt;
        sbi UCSRB,TXEN                    ; UART TX aktivieren&lt;br /&gt;
        ldi temp,4000000/(9600*16)-1      ; Baudrate 9600 einstellen&lt;br /&gt;
        out UBRR,temp&lt;br /&gt;
&lt;br /&gt;
start:&lt;br /&gt;
        ldi ZL, LOW(text*2)               ; Adresse des Strings in den&lt;br /&gt;
        ldi ZH, HIGH(text*2)              ; Z-Pointer laden&lt;br /&gt;
        rcall print                       ; Unterfunktion print aufrufen&lt;br /&gt;
&lt;br /&gt;
        ldi R16, 10                       ; die Bytes 10 und 13 senden&lt;br /&gt;
        rcall sendbyte                    ; (Zeilenumbruch im Terminal)&lt;br /&gt;
        ldi R16, 13&lt;br /&gt;
        rcall sendbyte&lt;br /&gt;
&lt;br /&gt;
        rjmp start                        ; das Ganze wiederholen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; print: sendet die durch den Z-Pointer adressierte Zeichenkette&lt;br /&gt;
print:&lt;br /&gt;
        lpm                               ; Erstes Byte des Strings nach R0 lesen&lt;br /&gt;
        tst R0                            ; R0 auf 0 testen&lt;br /&gt;
        breq print_end                    ; wenn 0, dann zu print_end&lt;br /&gt;
        mov r16, r0                       ; Inhalt von R0 nach R16 kopieren&lt;br /&gt;
        rcall sendbyte                    ; UART-Sendefunktion aufrufen&lt;br /&gt;
        adiw ZL, 1                        ; Adresse des Z-Pointers um 1 erhöhen&lt;br /&gt;
        rjmp print                        ; wieder zum Anfang springen&lt;br /&gt;
print_end:&lt;br /&gt;
        ret&lt;br /&gt;
&lt;br /&gt;
; sendbyte: sendet das Byte aus R16 über das UART&lt;br /&gt;
sendbyte:&lt;br /&gt;
        sbis UCSRA, UDRE                  ; warten bis das UART bereit ist&lt;br /&gt;
        rjmp sendbyte&lt;br /&gt;
        out UDR, R16&lt;br /&gt;
        ret&lt;br /&gt;
&lt;br /&gt;
text:&lt;br /&gt;
        .db &amp;quot;AVR-Assembler ist ganz einfach&amp;quot;,0    ; Stringkonstante, durch eine 0 abgeschlossen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Neuere AVR-Controller besitzen einen erweiterten Befehlssatz. Darunter befindet sich auch der folgende Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 lpm r16, Z+&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl liest ein Byte aus dem Flash und speichert es in einem beliebigen Register, hier r16. Danach wird der Zeiger Z um eins erhöht.&lt;br /&gt;
&lt;br /&gt;
Für die neuen Controller, wie ATmegas kann das Codebeispiel also so abgeändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
; print: sendet die durch den Z-Pointer adressierte Zeichenkette&lt;br /&gt;
print:&lt;br /&gt;
        lpm r16, z+                       ; Erstes Byte des Strings nach r16 lesen&lt;br /&gt;
        tst r16                           ; r16 auf 0 testen&lt;br /&gt;
        breq print_end                    ; wenn 0, dann zu print_end&lt;br /&gt;
        rcall sendbyte                    ; UART-Sendefunktion aufrufen&lt;br /&gt;
        rjmp print                        ; wieder zum Anfang springen&lt;br /&gt;
print_end:&lt;br /&gt;
        ret&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man bei .db einen Text in doppelten Anführungszeichen angibt, werden die Zeichen automatisch in die entsprechenden ASCII-Codes umgerechnet: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        .db &amp;quot;Test&amp;quot;, 0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist äquivalent zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        .db 84, 101, 115, 116, 0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit das Programm das Ende der Zeichenkette erkennen kann, wird eine 0 an den Text angehängt. &lt;br /&gt;
&lt;br /&gt;
Das ist doch schonmal sehr viel praktischer, als jeden Buchstaben einzeln in ein Register zu laden und abzuschicken. Und wenn man statt &#039;&#039;&#039;sendbyte&#039;&#039;&#039; einfach die Routine &#039;&#039;&#039;lcd_data&#039;&#039;&#039; aus dem 4. Teil des Tutorials aufruft, dann funktioniert das gleiche sogar mit dem LCD!&lt;br /&gt;
&lt;br /&gt;
==== Neue Assemblerbefehle ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
        lpm                            ; Liest das durch den Z-Pointer addressierte Byte&lt;br /&gt;
                                       ; aus dem Flash-ROM in das Register R0 ein. &lt;br /&gt;
        lpm [Register], Z              ; Macht das gleiche wie lpm, jedoch in ein beliebiges Register&lt;br /&gt;
        lpm [Register], Z+             ; Erhöht zusätzlich den Z-Zeiger&lt;br /&gt;
&lt;br /&gt;
        tst [Register]                 ; Prüft, ob der Inhalt eines Registers 0 ist. &lt;br /&gt;
&lt;br /&gt;
        breq [Label]                   ; Springt zu [Label], wenn der vorhergehende Vergleich wahr ist. &lt;br /&gt;
&lt;br /&gt;
        adiw [Register], [Konstante]   ; Addiert eine Konstante zu einem Registerpaar.&lt;br /&gt;
                                       ; [Register] bezeichnet das untere der beiden Register.&lt;br /&gt;
                                       ; Kann nur auf die Registerpaare R25:R24, R27:R26,&lt;br /&gt;
                                       ; R29:R28 und R31:R30 angewendet werden. &lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EEPROM ===&lt;br /&gt;
&lt;br /&gt;
==== Lesen ====&lt;br /&gt;
&lt;br /&gt;
Zuerst wird die EEPROM-Adresse von der gelesen werden soll in das IO-Registerpaar &#039;&#039;&#039;EEARH/EEARL&#039;&#039;&#039; (EEPROM Address Register) geladen. Da der ATmega8 mehr als 256 Byte RAM hat passt die Adresse nicht in ein einziges 8-Bit-Register, sondern muss in zwei Register aufgeteilt werden: EEARH bekommt das obere Byte der Adresse, EEARL das untere Byte.&lt;br /&gt;
&lt;br /&gt;
Dann löst man den Lesevorgang durch das Setzen des Bits &#039;&#039;&#039;EERE&#039;&#039;&#039; (EEPROM Read Enable) im IO-Register &#039;&#039;&#039;EECR&#039;&#039;&#039; (EEPROM Control Register) aus. Da das EEPROM eine größere Zugriffszeit als das Flash besitzt, wird anschließend eine Schleife eingefügt, die solange wartet, bis das EEPROM signalisiert, dass die Daten jetzt verfügbar sind. Das gelesene Byte kann dann aus dem IO-Register &#039;&#039;&#039;EEDR&#039;&#039;&#039; (EEPROM Data Register) in ein normales Arbeitsregister kopiert und von dort weiterverarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Doch um etwas aus dem EEPROM lesen zu können, muss man natürlich erst mal Daten hineinbekommen.&lt;br /&gt;
&lt;br /&gt;
Wie auch das Flash-ROM kann man das EEPROM über den ISP-Programmer programmieren. Die Daten, die im EEPROM abgelegt werden sollen, werden wie gewohnt mit .db angegeben; allerdings muss man dem Assembler natürlich sagen, dass es sich hier um Daten für das EEPROM handelt. Das macht man durch die Direktive &#039;&#039;&#039;.eseg&#039;&#039;&#039;, woran der Assembler erkennt, dass alle nun folgenden Daten für das EEPROM bestimmt sind. &lt;br /&gt;
&lt;br /&gt;
Damit man die Bytes nicht von Hand abzählen muss um die Adresse herauszufinden, kann man auch im EEPROM-Segment wieder Labels einsetzen und diese im Assemblerprogramm wie Konstanten verwenden. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        ldi R16, 0xFF&lt;br /&gt;
        out DDRB, R16                     ; Port B: Ausgang&lt;br /&gt;
&lt;br /&gt;
        ldi r16, HIGH(daten)              ; Adresse laden&lt;br /&gt;
        out EEARH, r16 &lt;br /&gt;
        ldi r16, LOW(daten)&lt;br /&gt;
        out EEARL, r16&lt;br /&gt;
        &lt;br /&gt;
        sbi EECR, EERE                    ; Lesevorgang aktivieren&lt;br /&gt;
&lt;br /&gt;
        in R16, EEDR&lt;br /&gt;
        &lt;br /&gt;
        out PORTB, R16&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp loop&lt;br /&gt;
&lt;br /&gt;
.eseg&lt;br /&gt;
daten:&lt;br /&gt;
        .db 0b10101010&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dieses Programm assembliert, erhält man außer der .hex-Datei noch eine Datei mit der Endung &#039;&#039;&#039;.eep&#039;&#039;&#039;. Diese Datei enthält die Daten aus dem EEPROM-Segment (.eseg), und muss zusätzlich zu der hex-Datei in den Controller programmiert werden. &lt;br /&gt;
&lt;br /&gt;
Das Programm gibt die Binärzahl 0b10101010 an den Port B aus, das heißt jetzt sollte jede zweite LED leuchten.&lt;br /&gt;
&lt;br /&gt;
Natürlich kann man auch aus dem EEPROM Strings lesen und an den UART senden: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;color:red; font-weight: bold;&amp;quot;&amp;gt;Hinweis: Dieses Beispiel wurde noch nicht für den ATmega8 angepasst.&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
.include &amp;quot;4433def.inc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
.def temp             = r16&lt;br /&gt;
.def address          = r17&lt;br /&gt;
.def data             = r18&lt;br /&gt;
&lt;br /&gt;
        ldi temp, RAMEND&lt;br /&gt;
        out SPL, temp&lt;br /&gt;
&lt;br /&gt;
        sbi UCSRB,TXEN                    ; UART TX aktivieren&lt;br /&gt;
        ldi temp,4000000/(9600*16)-1      ; Baudrate 9600 einstellen&lt;br /&gt;
        out UBRR,temp&lt;br /&gt;
&lt;br /&gt;
        ldi address, text1                ; ersten String senden&lt;br /&gt;
        rcall eep_print&lt;br /&gt;
        &lt;br /&gt;
        ldi address, text2                ; zweiten String senden&lt;br /&gt;
        rcall eep_print&lt;br /&gt;
&lt;br /&gt;
        ldi data, 10                      ; die Bytes 10 und 13 senden&lt;br /&gt;
        rcall sendbyte                    ; (Zeilenumbruch im Terminal)&lt;br /&gt;
        ldi data, 13&lt;br /&gt;
        rcall sendbyte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
loop:   rjmp loop&lt;br /&gt;
        &lt;br /&gt;
eep_print:&lt;br /&gt;
        out EEAR, address                 ; EEPROM-Adresse&lt;br /&gt;
        sbi EECR, EERE                    ; Lesevorgang starten&lt;br /&gt;
        in data, EEDR                     ; gelesenes Byte nach &amp;quot;data&amp;quot;&lt;br /&gt;
        tst data                          ; auf 0 (Stringende testen)&lt;br /&gt;
        breq eep_print_end                ; falls 0, Funktion beenden&lt;br /&gt;
        rcall sendbyte                    ; ansonsten Byte senden...&lt;br /&gt;
        inc address                       ; ... Adresse um 1 erhöhen...&lt;br /&gt;
        rjmp eep_print                    ; ... und zum Anfang der Funktion&lt;br /&gt;
eep_print_end:&lt;br /&gt;
        ret&lt;br /&gt;
&lt;br /&gt;
; sendbyte: sendet das Byte aus &amp;quot;data&amp;quot; über den UART&lt;br /&gt;
sendbyte:&lt;br /&gt;
        sbis UCSRA, UDRE                  ; warten bis das UART bereit ist&lt;br /&gt;
        rjmp sendbyte&lt;br /&gt;
        out UDR, data&lt;br /&gt;
        ret&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.eseg&lt;br /&gt;
text1:&lt;br /&gt;
        .db &amp;quot;Strings funktionieren auch &amp;quot;, 0&lt;br /&gt;
text2:&lt;br /&gt;
        .db &amp;quot;im EEPROM&amp;quot;, 0&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Schreiben ====&lt;br /&gt;
&lt;br /&gt;
Als erstes muss geprüft werden, ob ein vorheriger Schreibzugriff schon abgeschlossen ist. Danach wird die EEPROM-Adresse, auf die geschrieben wird, in das IO-Register &#039;&#039;&#039;EEAR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;A&#039;&#039;&#039;ddress &#039;&#039;&#039;R&#039;&#039;&#039;egister) geladen. Dann schreibt man die Daten, welche man auf der im Adressregister abgespeicherten Position ablegen will ins Register &#039;&#039;&#039;EEDR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;R&#039;&#039;&#039;egister). Als nächstes setzt man das &#039;&#039;&#039;EEMWE&#039;&#039;&#039; Bit im EEPROM-Kontrollregister &#039;&#039;&#039;EECR&#039;&#039;&#039; (&#039;&#039;&#039;EE&#039;&#039;&#039;PROM &#039;&#039;&#039;C&#039;&#039;&#039;ontrol &#039;&#039;&#039;R&#039;&#039;&#039;egister) um den Schreibvorgang vorzubereiten. Nun wird es zeitkritisch - es darf nun keinesfalls ein Interrupt dazwischenfahren - denn man muss innerhalb von 4 Taktzyklen das &#039;&#039;&#039;EEWE&#039;&#039;&#039; Bit setzen um den Schreibvorgang auszulösen. Um das sicherzustellen werden die Interrupts kurz gesperrt. Danach startet der Schreibvorgang und läuft automatisch ab. Wenn er beendet ist, wird von der Hardware das &#039;&#039;&#039;EEPE&#039;&#039;&#039; Bit im Register &#039;&#039;&#039;EECR&#039;&#039;&#039; wieder gelöscht.&lt;br /&gt;
&lt;br /&gt;
Hier ein Code für das Schreiben in den EEPROM (durch UART-Interrupt ausgelöst) dazu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
include &amp;quot;m8def.inc&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
.def temp = R16&lt;br /&gt;
.def sreg_save = r17&lt;br /&gt;
&lt;br /&gt;
.equ CLOCK = 4000000&lt;br /&gt;
.equ BAUD = 9600&lt;br /&gt;
.equ UBRRVAL = CLOCK/(BAUD*16)-1&lt;br /&gt;
 &lt;br /&gt;
; hier geht das Programmsegment los&lt;br /&gt;
.CSEG&lt;br /&gt;
.org 0x00&lt;br /&gt;
        rjmp    main&lt;br /&gt;
 &lt;br /&gt;
.org URXCaddr&lt;br /&gt;
        rjmp    int_rxc&lt;br /&gt;
 &lt;br /&gt;
; Hauptprogramm&lt;br /&gt;
main:&lt;br /&gt;
        ldi     temp, LOW(RAMEND)               ; Stackpointer initialisieren&lt;br /&gt;
        out     SPL, temp&lt;br /&gt;
        ldi     temp, HIGH(RAMEND)&lt;br /&gt;
        out     SPH, temp&lt;br /&gt;
                              &lt;br /&gt;
        ldi     temp, LOW(UBRRVAL)              ; Baudrate einstellen&lt;br /&gt;
        out     UBRRL, temp&lt;br /&gt;
        ldi     temp, HIGH(UBRRVAL)&lt;br /&gt;
        out     UBRRH, temp&lt;br /&gt;
 &lt;br /&gt;
                                       &lt;br /&gt;
        ldi     temp, (1&amp;lt;&amp;lt;URSEL)|(3&amp;lt;&amp;lt;UCSZ0)     ; Frame-Format: 8 Bit&lt;br /&gt;
        out     UCSRC, temp&lt;br /&gt;
 &lt;br /&gt;
        sbi     UCSRB, RXCIE                    ; Interrupt bei Empfang&lt;br /&gt;
        sbi     UCSRB, RXEN                     ; RX (Empfang) aktivieren&lt;br /&gt;
        &lt;br /&gt;
        sei                                     ; Interrupts global aktivieren&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
loop:   &lt;br /&gt;
        rjmp    loop                            ; Endlosschleife(ABER Interrupts!)&lt;br /&gt;
        &lt;br /&gt;
; Interruptroutine wird ausgeführt, sobald ein Byte über den UART empfangen wurde&lt;br /&gt;
&lt;br /&gt;
int_rxc:&lt;br /&gt;
        push    temp                            ; temp auf dem Stack sichern&lt;br /&gt;
        in      temp,sreg                       ; SREG sicher, muss praktisch in jeder&lt;br /&gt;
                                                ; Interruptroutine gemacht werden&lt;br /&gt;
        push    temp&lt;br /&gt;
        &lt;br /&gt;
        in      temp, UDR                       ; empfangenes Byte lesen&lt;br /&gt;
    &lt;br /&gt;
        rcall   EEPROM_write                    ; Byte im EEPROM speichern&lt;br /&gt;
&lt;br /&gt;
        pop     temp&lt;br /&gt;
        out     sreg,temp&lt;br /&gt;
        pop     temp                            ; temp wiederherstellen&lt;br /&gt;
reti&lt;br /&gt;
&lt;br /&gt;
; der eigentliche EEPROM Schreibzugriff&lt;br /&gt;
&lt;br /&gt;
EEPROM_write:&lt;br /&gt;
        sbic    EECR, EEPE                  ; prüfe ob der letzte Schreibvorgang beendet ist&lt;br /&gt;
        rjmp    EEPROM_write                ; wenn nein, nochmal prüfen&lt;br /&gt;
&lt;br /&gt;
        ldi     temp, HIGH(Daten)           ; High-Adresse im EEPROM laden&lt;br /&gt;
        out     EEARH, temp                 ; und ins EEARH schreiben&lt;br /&gt;
        ldi     temp, LOW(Daten)            ; Low-Adresse im EEPROM laden&lt;br /&gt;
        out     EEARL, temp                 ; und ins EEARL schreiben&lt;br /&gt;
        out     EEDR,temp                   ; Daten ins EEPROM-Datenregister&lt;br /&gt;
&lt;br /&gt;
        in      sreg_save,sreg              ; SREG sichern&lt;br /&gt;
        cli                                 ; Interrupts sperren, die nächsten&lt;br /&gt;
                                            ; zwei Befehle dürfen NICHT&lt;br /&gt;
                                            ; unterbrochen werden&lt;br /&gt;
        sbi     EECR,EEMWE                  ; Schreiben vorbereiten&lt;br /&gt;
        sbi     EECR,EEWE                   ; Und los !&lt;br /&gt;
        out     sreg, sreg_save             ; SREG wieder herstellen&lt;br /&gt;
        ret&lt;br /&gt;
&lt;br /&gt;
; hier wird der EEPROM-Inhalt definiert&lt;br /&gt;
.ESEG&lt;br /&gt;
&lt;br /&gt;
Daten:  .db     0,0,0,0&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist jetzt sehr einfach gehalten, aber für Anfänger leicht auf ihr Projekt zu übertragen.&lt;br /&gt;
&lt;br /&gt;
=== SRAM ===&lt;br /&gt;
&lt;br /&gt;
Die Verwendung des SRAM wird in einem späteren Kapitel erklärt: [[AVR-Tutorial: SRAM]]&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Adressierung]]&lt;br /&gt;
&lt;br /&gt;
{{Navigation_zurückhochvor|&lt;br /&gt;
zurücktext=UART|&lt;br /&gt;
zurücklink=AVR-Tutorial: UART|&lt;br /&gt;
hochtext=Inhaltsverzeichnis|&lt;br /&gt;
hochlink=AVR-Tutorial|&lt;br /&gt;
vortext=Timer|&lt;br /&gt;
vorlink=AVR-Tutorial: Timer}}&lt;br /&gt;
&lt;br /&gt;
[[Category:AVR]][[Category:AVR-Tutorial]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25182</id>
		<title>Interrupt</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25182"/>
		<updated>2007-12-22T13:16:36Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Steuersignale zwischen ISR und Hauptprogramm */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein &#039;&#039;&#039;Interrupt&#039;&#039;&#039; ausgelöst. Dabei wird das Programm unterbrochen und ein Unterprogramm, die sogenannte &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine (&#039;&#039;&#039;ISR&#039;&#039;&#039;), aufgerufen. Wenn dieses beendet ist läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
Bei [[Mikrocontroller]]n werden Interrupts z.B. ausgelöst wenn:&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Pegel ändert&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine [[Seriell|serielle]] Übertragung abgeschlossen ist&lt;br /&gt;
* eine Messung des [[ADC |Analog-Digital-Wandlers]] abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
== Wichtige Eigenschaften von ISRs ==&lt;br /&gt;
&lt;br /&gt;
ISRs reagieren auf ein bestimmtes Ereignis, welches relativ oft oder selten passiert. Prinzipiell sollte man ISRs möglichst kurz halten und schnell beenden. &#039;&#039;&#039;Im Mittel muss die Interruptroutine kürzer sein als die Periodendauer des Ereignises, anderenfalls wird es passieren, dass Interrupts &amp;quot;verschluckt&amp;quot; werden&#039;&#039;&#039;, d.h. beim UART gehen Daten verloren, beim Timer gehen Zählzyklen verloren, beim AD-Wandler gehen Daten verloren etc. &#039;&#039;&#039;Solche verschluckten Interrupts sind bisweilen schwer zu finden, weil es bisweilen nur sehr wenige in ganz bestimmten Konstellationen sind.&#039;&#039;&#039; Wenn dann eine per Timer realisierte Uhr in der Stunde um 1s falsch geht, merkt man das oft nicht. &#039;&#039;&#039;Langwierige Berechnungen, Auswertungen, Ausgaben oder gar Warteschleifen haben in ISRs nichts zu suchen.&#039;&#039;&#039; Typische C-Funktionen wie printf(), scanf(), längere Ausgaben auf ein [[LCD]] etc. sollte man nicht in ISRs vornehmen. Hier kommt sinnvollerweise eine andere Programmiertechnik zu Einsatz, nämlich die Übergabe von Parametern bzw. Steuersignalen an das Hauptprogramm.&lt;br /&gt;
&lt;br /&gt;
=== Verschachtelte Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Einige Mikrocontroller, wie z.B. der [[AVR]] kennen nur zwei CPU-Zustände. Normale Programmausführung und Interruptausführung, gesteuert durch das I-Bit der CPU. Die normale Programmausführung kann jederzeit durch Interrupts unterbrochen werden. Die Interruptausführung kann nicht durch neue Interrupts unterbrochen werden. Die ISR wird erst zu Ende bearbeitet, zurück in die normale Programmausführung gesprungen und erst dann werden neue, wartende (engl. pending) Interrupts bearbeitet.&lt;br /&gt;
&lt;br /&gt;
Etwas komplexere Mikrocontroller oder grosse Prozessoren bieten verschiedene Interruptlevel (Stufen) an . Dabei gilt meist je niedriger die Zahl des Levels, um so höher die Priorität. Ein Interrupt mit höherer Priorität kann einen Interrupt mit niedriger Priorität unterbrechen. Ein Interrupt mit gleicher Priorität wie der gerade bearbeitete Interrupt kann das im allgemeinen nicht. Das nennt man verschachtelte Interrupts (engl. nested interrupts). Klassische Vertreter hierfür sind [[8051]], PowerPC, [[X86]] und Motorola 68000.&lt;br /&gt;
&lt;br /&gt;
Auf dem AVR kann man [[AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen | verschachtelte Interrupts]] sowohl in Assembler als auch in C nachbilden, allerdings mit einigen Einschränkungen und Tücken. Das ist jedoch Leuten vorbehalten, die schon viel Erfahrung auf diesem Gebiet haben. Zu 99,9% braucht man sie nicht.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten eines Timerinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein Timerinterrupt wird im allgemeinen dazu genutzt, in konstanten, periodischen Abständen bestimmte Funktionen aufzurufen. Es ist möglich, dass während eines Timerinterrupts derselbe Interrupt wieder aktiv wird, weil die Routine sehr verzweigt ist und dieses Mal sehr lange dauert.&lt;br /&gt;
&lt;br /&gt;
Wenn zum Beispiel der Timerinterrupt mit einer Periodendauer von 100ms aufgerufen wird, er aber unter bestimmten Umständen 180ms benötigt, dann wird nach 100ms nach Eintritt in die ISR der Interrupt wieder aktiv, das Timer Interrupt Flag wird gesetzt. Da aber gerade ein Interrupt bearbeitet wird, wird er nicht sofort angesprungen, weil währenddessen die Interruptfunktion global gesperrt ist (beim AVR ist das I-Bit in der CPU gelöscht). Der Interrupt wird zu Ende bearbeitet, die CPU springt zurück zum Hauptprogramm. Dabei werden die Interrupts wieder global eingeschaltet. Der zwischenzeitlich eingetroffene und zwischengespeicherte Interrupt wird nun sofort ausgeführt, sodass das Hauptprogramm praktisch gar nicht weiter kommt, bestenfalls einen Maschinenbefehl. Nun sind aber nur noch 20ms bis zum nächsten Timerinterrupt übrig. Wenn dieser nun wieder 180 ms benötigt werden in dieser Zeit aber &#039;&#039;&#039;zwei&#039;&#039;&#039; Interrupts ausgelöst, nach 20ms und 120ms. Da diese aber nicht gezählt oder andersweitig einzeln gespeichert werden können, geht ein Interrupt verloren. Das ist ein Programmfehler.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten des UART Empfangsinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein [[UART]] Interrupt zum Empfang von Daten per [[RS232]] mit 115200 [[Baud]] ist ein recht häufiges Ereignis (1 Zeichen = 10 Bits = 86,8&amp;amp;mu;s). Wenn kontinuierlich Daten empfangen werden, wird nach jeweils 86,8&amp;amp;mu;s ein neuer Interrupt ausgelöst. Dabei wird das empfangene Datenbyte vom UART aus dem Empfangsschiebegregister in einem Puffer kopiert. Während das neue Zeichen Bit für Bit empfangen wird, wird es zunächst im Schieberegister des UART gespeichert. Die Daten im Puffer bleiben davon unberührt. Die CPU muss nun schnell das empfangene Datenbyte aus dem Empfangsbuffer auslesen. Die maximale Verzögerung, die sich die CPU erlauben kann von der Aktivierung des Interrupts bis zum tatsächlichen Auslesen des Datenregisters beträgt ziemlich genau die Übertragungszeit von einem Zeichen. Wenn bis dahin nicht das Zeichen von der CPU ausgelesen wurde, wird es vom UART überschrieben und ein Fehler im Statusregister des UART signalisiert (Overrun, Überlauf des Datenpuffers). Die UARTs in heutigen Mikrocontrollern haben mindestens ein Byte Puffer wie hier beschrieben. Die neueren [[AVR]]s haben sogar effektiv 3 Byte Puffer im UART, praktisch ein kleines [[FIFO]], womit der Datenempfang besser gepuffert werden kann, wenn die CPU gerade mit anderen sehr wichtigen Dingen beschäftigt ist. D.h. kurzzeitig kann sich die CPU erlauben, die Übertragungszeit von bis zu drei Zeichen zu warten, ehe sie die Daten ausliest. Dann müssen sie aber sehr schnell hintereinander gelesen werden. Im Mittel hat die CPU aber nur die Übertragungszeit eines Zeichens zur Verfügung, um es abzuholen.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenfassung ===&lt;br /&gt;
&lt;br /&gt;
Interruptserviceroutinen:&lt;br /&gt;
* sollten so kurz wie möglich gehalten werden&lt;br /&gt;
* können im Einzelfall nahezu doppelt so lange dauern wie die kürzeste Periodendauer des Ereignisses, ohne dass Interrupts verloren gehen (z.B. Timerinterrupt).&lt;br /&gt;
* dürfen im Mittel maximal solange dauern wie die kürzeste Periodendauer des Ereignisses&lt;br /&gt;
* dürfen maximal solange dauern, wie die kürzeste Periodendauer des Ereignisses, wenn man auf Nummer sicher gehen will, dass keine Interrupts verschluckt werden&lt;br /&gt;
* Die Interruptzeit versteht sich immer abzüglich einer kleinen Reserve für das Anspringen und Verlassen des Interrupt minus Panikreserve&lt;br /&gt;
&lt;br /&gt;
== Beispiele für die praktische Programmierung ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Als Mikrocontroller wird ein AVR vom Typ ATmega32 verwendet. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== Steuersignale zwischen ISR und Hauptprogramm ===&lt;br /&gt;
&lt;br /&gt;
In vielen Anwendungen wird ein Timer verwendet, um in regelmässigen Abständen bestimmte Aktionen auszuführen, wie z.B. Tasten abfragen, ADC-auslesen, ein LCD auffrischen etc. Wenn viele Dinge zu erledigen sind, nebenbei aber noch andere Interrupts verwendet werden, dann ist es notwendig die Funktionsaufrufe aus dem Timerinterrupt in die Hauptschleife zu verlagern. Der Interrupt signalisiert über eine Steuervariable (engl. Flag, Flagge), das ein neuer Timerzyklus begonnen hat. Dadurch wird der Timerinterrupt sehr kurz und die langwierigen, aber meist nicht zeitkritischen Funktionen werden als normales Programm ausgeführt. Damit kann die CPU auf andere Interrupts schnell reagieren.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auf jeden Fall, dass die Steuervariable, welche in der ISR und in der Hauptschleife verwendet wird, mit [http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword &#039;&#039;&#039;volatile&#039;&#039;&#039;] deklariert wird. Ausserdem muss sowohl der Lese- als auch Schreibzugriff auf die Steuersignale [[#Atomarer Datenzugriff | &#039;&#039;&#039;atomar&#039;&#039;&#039;]] sein. Auf dem AVR ist das mit 8 Bit Variablen direkt möglich, für grössere Variablen müssen die Interrupts kurzzeitig gesperrt werden.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel ist sehr einfach gehalten um das Prinzip zu veranschaulichen. Ein Timer mit einer Überlaufperiodendauer von ca. 65ms stösst periodisch eine Funktion zum Togglen einer LED an, welche dadurch mit ca. 7 Hz blinkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Timer Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
 &lt;br /&gt;
int main() {&lt;br /&gt;
 &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    TCCR2  = 6;                     // Vorteiler 256 -&amp;gt; ~65ms Überlaufperiode&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer Overflow Interrupt freischalten &lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
        if (flag == 1) { // Neuer Timerzyklus ?&lt;br /&gt;
            flag = 0;&lt;br /&gt;
 &lt;br /&gt;
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) &lt;br /&gt;
            PORTD ^= (1 &amp;lt;&amp;lt; PD5);    // LED toggeln&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER2_OVF_vect ) {&lt;br /&gt;
    flag = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UART mit Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Der UART ist ein oft benutztes Modul eines Mikrocontrollers. Anfänger nutzen ihn meist im sogenannten Polling Betrieb (engl. to poll, abfragen). D.h. wenn ein Zeichen empfangen werden soll, fragt eine Funktion den UART in einer Schleife ununterbrochen ab, ob Daten empfangen wurden. In dieser Zeit macht die CPU nichts anderes! Und wenn lange kein Zeichen eintrifft tut sie sehr lange nichts, sie ist praktisch blockiert! Senden verläuft ähnlich, nur dass hier die CPU vor dem Senden prüft, ob der UART ein neues Byte aufnehmen kann. D.h. während der UART selbsttätig das Zeichen sendet ist die CPU zum Warten verdammt. All diese Nachteile haben nur einen Vorteil. Die Funktionen und Mechanismen zur UART-Nutzung sind sehr einfach, klein und leicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
Will man aber die CPU nicht sinnlos warten lassen, was vor allem bei niedrigeren Baudraten ziemlich lange sein kann, muss man die Interrupts nutzen. Der AVR hat gleich drei davon.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RXC&#039;&#039;&#039; (Receive Complete): Ein Zeichen wurde empfangen.&lt;br /&gt;
* &#039;&#039;&#039;UDRE&#039;&#039;&#039; (UART Data Register Empty): Der Zwischenpuffer des Senders ist leer und kann ein neues Zeichen aufnehmen. Dieser Zwischenpuffer ist wichtig, um lückenlos auch bei hohen Baudraten senden zu können.&lt;br /&gt;
* &#039;&#039;&#039;TXC&#039;&#039;&#039; (Transmit Complete): Das aktuelle Zeichen wurde vollständig inclusive Stopbit gesendet und es liegt kein neues Datenbyte im Sendepuffer. Dieser Interrupt ist extrem nützlich für eine Halbduplexkommunikation, z.B. auf einem RS485-[[Bus]]. Hier kann man nach dem vollständigen Senden aller Bytes den Bustranceiver (z.B. MAX485) von Senden auf Empfangen umschalten, um den Bus freizugeben.&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung der Interrupts kann die CPU andere Dinge bearbeiten und muss nur kurz einen Interrupt ausführen, wenn ein Zeichen empfangen oder gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Kommunikation zwischen ISRs und Hauptschleife erfolgt wieder durch Flags und zwei Pufferarrays (uart_rx_buffer und uart_tx_buffer). Es gibt zwei Funktionen, eine zum Senden von Strings, eine zum Empfangen. Das Senden sowie Empfangen kann parallel erfolgen und läuft vollkommen unabhängig vom Hauptprogramm. Die Daten werden in spezielle Puffer kopiert, sodass das Hauptprogramm mit seinen Strings sofort weiterarbeiten kann. Im Beispiel ist die CPU nicht wirklich mit sinnvollen Dingen beschäftigt, zur Demonstration des Prinzips aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Um das das Programm real zu nutzen braucht man ein Terminalprogramm, z.B. Hyperterminal von Windows. Dort muss nur die richtige Baudrate eingestellt werden (9600 8N1, 9600 Baud, 8 Bits, keine Parität, 1 Stopbit, keine Flusskontrolle). Ausserdem muss man im Menu Datei -&amp;gt; Eigenschaften -&amp;gt; Einstellungen -&amp;gt; ASCII Konfiguration den Punkt &amp;quot;Eingegebene Zeichen lokal ausgeben (lokales Echo)&amp;quot; aktivieren. Nun kann man beliebige Texte eintippen. Mit RETURN wird die Eingabe abgeschlossen und der AVR vermittelt den empfangenen String an das Hauptprogramm. Diese sendet ihn einfach zurück, paralle dazu wird der String gemorst per LED angezeigt. Sollte es Probleme bei der Inbetriebnahme des UART geben, so findet man hier wichtige Hinweise zur [[AVR_Checkliste#UART.2FUSART | Fehlersuche]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* UART Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0/PD1 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen/senden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
// Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define F_CPU 3686400L               &lt;br /&gt;
// &amp;quot;Morsedauer&amp;quot; für ein Bit in Millisekunden&lt;br /&gt;
#define BITZEIT 100     &lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen für den UART&lt;br /&gt;
 &lt;br /&gt;
// Puffergrösse in Bytes, RX und TX sind gleich gross&lt;br /&gt;
#define uart_buffer_size 32             &lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t uart_rx_flag=0;            // Flag, String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_tx_flag=1;            // Flag, String komplett gesendet&lt;br /&gt;
char uart_rx_buffer[uart_buffer_size];      // Empfangspuffer&lt;br /&gt;
char uart_tx_buffer[uart_buffer_size];      // Sendepuffer&lt;br /&gt;
 &lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen String senden&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_t_flag==1 ist&lt;br /&gt;
// nur dann kann ein neuer String gesendet werden&lt;br /&gt;
 &lt;br /&gt;
void put_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_tx_flag==1) {&lt;br /&gt;
      // String daten ind en Sendepuffer kopieren&lt;br /&gt;
      strcpy(uart_tx_buffer, daten);      &lt;br /&gt;
      // Flag für &#039;Senden ist komplett&#039; löschen, &lt;br /&gt;
      uart_tx_flag = 0;                    &lt;br /&gt;
      // UDRE Interrupt einschalten, los gehts&lt;br /&gt;
      UCSRB |= (1&amp;lt;&amp;lt;UDRIE); &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen empfangenen String kopieren&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_rx_flag==1 ist&lt;br /&gt;
// anderenfalls ist der RX Buffer noch ungültig&lt;br /&gt;
 &lt;br /&gt;
void get_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_rx_flag==1) {&lt;br /&gt;
      // String kopieren&lt;br /&gt;
      strcpy(daten, uart_rx_buffer);      &lt;br /&gt;
      // Flag löschen&lt;br /&gt;
      uart_rx_flag = 0;                    &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Ein Byte im RS232 Format auf eine LED ausgeben&lt;br /&gt;
 &lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startbit, immer 0&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);           // LED aus&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
 &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)            // Prüfe Bit #0&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        else&lt;br /&gt;
            PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus      &lt;br /&gt;
        long_delay(BITZEIT);        &lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;                 // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
    // Stopbit, immer 1&lt;br /&gt;
    PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED an&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
int main (void) {&lt;br /&gt;
 &lt;br /&gt;
    char stringbuffer[64];  // Allgemeiner Puffer für Strings&lt;br /&gt;
    uint8_t buffer_full=0;  // noch ein Flag, aber nur in der Hauptschleife&lt;br /&gt;
    char * charpointer;     // Hilfszeiger&lt;br /&gt;
    &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
 &lt;br /&gt;
// Stringpuffer initialisieren&lt;br /&gt;
 &lt;br /&gt;
    stringbuffer[0] = &#039;\n&#039;;&lt;br /&gt;
    stringbuffer[1] = &#039;\r&#039;;&lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
 &lt;br /&gt;
        // &amp;quot;Sinnvolle&amp;quot; CPU Tätigkeit &lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
    &lt;br /&gt;
        // Wurde ein kompletter String empfangen &lt;br /&gt;
        // und der Buffer ist leer?&lt;br /&gt;
        if (uart_rx_flag==1 &amp;amp;&amp;amp; buffer_full==0) {    &lt;br /&gt;
            // ja, dann String lesen, &lt;br /&gt;
            // die ersten zwei Zeichen &lt;br /&gt;
            // aber nicht überschreiben&lt;br /&gt;
            get_string(stringbuffer+2);             &lt;br /&gt;
            buffer_full=1;&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        // Ist letzte Stringsendung abgeschlossen &lt;br /&gt;
        // und ein neuer String verfügbar?&lt;br /&gt;
        if (uart_tx_flag==1 &amp;amp;&amp;amp; buffer_full==1) {    &lt;br /&gt;
            // Newline + Carrige return anfügen&lt;br /&gt;
            strcat(stringbuffer, &amp;quot;\n\r&amp;quot;);           &lt;br /&gt;
            put_string(stringbuffer); // zurücksenden&lt;br /&gt;
            buffer_full=0; // Buffer ist wieder verfügbar&lt;br /&gt;
            // Alle Zeichen per LED morsen&lt;br /&gt;
            charpointer = stringbuffer;&lt;br /&gt;
            while(*charpointer) morse(*charpointer++);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
 &lt;br /&gt;
// hier werden Daten vom PC empfangen und in einem String zwischengespeichert&lt;br /&gt;
// Wird ein Stringterminator empfangen, wird ein Flag gesetzt, welches dem &lt;br /&gt;
// Hauptprogramm den kompletten Empfang signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    &lt;br /&gt;
    static uint8_t uart_rx_cnt;     // Zähler für empfangene Zeichen&lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // Daten auslesen, dadurch wird das Interruptflag gelöscht              &lt;br /&gt;
    data = UDR;&lt;br /&gt;
    &lt;br /&gt;
    // Ist Puffer frei für neue Daten? &lt;br /&gt;
    if (!uart_rx_flag) {&lt;br /&gt;
        // ja, ist Ende des Strings (RETURN) erreicht?&lt;br /&gt;
        if (data==&#039;\r&#039;) {&lt;br /&gt;
            // ja, dann String terminieren&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=0;              &lt;br /&gt;
            // Flag für &#039;Empfangspuffer voll&#039; setzen&lt;br /&gt;
            uart_rx_flag=1;&lt;br /&gt;
            // Zähler zurücksetzen&lt;br /&gt;
            uart_rx_cnt=0;&lt;br /&gt;
        }&lt;br /&gt;
        else if (uart_rx_cnt&amp;lt;(uart_buffer_size-1)) {     &lt;br /&gt;
            // Daten in Puffer speichern&lt;br /&gt;
            // aber durch if() Pufferüberlauf vermeiden&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=data;          &lt;br /&gt;
            uart_rx_cnt++; // Zähler erhöhen&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART TX data register empty interrupt&lt;br /&gt;
// hier werden neue Daten in das UART Senderegister geladen&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_UDRE_vect) {&lt;br /&gt;
    // Zeiger auf Sendepuffer&lt;br /&gt;
    static char* uart_tx_p = uart_tx_buffer;    &lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // zu sendendes Zeichen lesen, &lt;br /&gt;
    // Zeiger auf Sendepuffer erhöhen&lt;br /&gt;
    data = *uart_tx_p++;&lt;br /&gt;
    &lt;br /&gt;
    // Ende des nullterminierten Strings erreicht?&lt;br /&gt;
    if (data==0 ) {        &lt;br /&gt;
        UCSRB &amp;amp;= ~(1&amp;lt;&amp;lt;UDRIE);       // ja, dann UDRE Interrupt ausschalten        &lt;br /&gt;
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen&lt;br /&gt;
        uart_tx_flag = 1;           // Flag setzen, Übertragung beeendet&lt;br /&gt;
    }&lt;br /&gt;
    else UDR = data;                // nein, Daten senden&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie lange dauert meine Interruptroutine ? ===&lt;br /&gt;
&lt;br /&gt;
Diese Frage sollte man beantworten können, zumindest sollte eine Worst Case Abschätzung gemacht werden. Das geht auf zwei Wegen.&lt;br /&gt;
&lt;br /&gt;
* Simulation, dabei muss in einer verzweigten ISR der längste Pfad simuliert werden. Dazu müssen alle beteiligten Variablen auf den ensprechenden Wert gesetzt werden.&lt;br /&gt;
* Messung mit dem [[Oszilloskop]], dabei wird zum Beginn der ISR ein Pin auf HIGH gesetzt und am Ende auf LOW. Damit kann man in Echtzeit die Dauer der ISR messen. Die zusätzlichen Taktzyklen zum Aufruf und verlassen der ISR sind konstant und im wesentlichen bekannt. Mit einem modernen Digitaloszilloskop und dem &amp;quot;Infinite Persistence Mode&amp;quot; kann man eine Worst Case Messung vornehmen&lt;br /&gt;
&lt;br /&gt;
Als Hilfmittel zur Fehlersuche kann man auch am Ende der ISR prüfen, ob das jeweilige Interrupt-Request Bit schon wieder gesetzt ist. Wenn ja, dann ist die ISR in den meisten Fällen zu lang. Auch hier kann man einen Ausgang auf HIGH setzen und somit den Fehler anzeigen.&lt;br /&gt;
&lt;br /&gt;
==Interruptfeste Programmierung==&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Datenzugriff ===&lt;br /&gt;
&lt;br /&gt;
Von einem atomaren (engl. atomic) Datenzugriff spricht man, wenn der Zugriff innerhalb einer nicht unterbrechbaren Maschinenanweisung abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel Sorgfalt zu behandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
übersetzt sich auf AVR Prozessoren in&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  IN  r16,port&lt;br /&gt;
  ORI r16,0x03&lt;br /&gt;
  OUT port,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren, da der OUT Befehl den alten Zustand vor dem Interrupt wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gefährlich ist das insbesondere deshalb, weil der Fall nur selten auftritt und dies Verhalten sehr schlecht reproduzierbar ist.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei verschiedenen Prozessor-Architekturen tritt das Problem verschieden häufig auf. So übersetzt sich obiger Code bei MSP430 Prozessoren in einen einzelnen Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  OR  #0x03,port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
und stellt somit kein Problem dar. Im Zweifel hilft nur ein Blick in den erzeugten Assembler-Code. Bei der Übernahme fremden Codes ist dies zu beachten. Was bei i51 kein Problem war, kann bei AVR zu einem Problem werden, unter Umständen sogar abhängig von verwendeten Port sein.&lt;br /&gt;
&lt;br /&gt;
Ein ähnliches Problem entsteht bei Variablen, deren Grösse die Wortbreite der Maschine übersteigt. Bei 8-Bit-Prozessoren wie AVR oder i51 also bereits bei normalen &amp;quot;int&amp;quot; Variablen. Diese Variablen werden zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal versehentlich die Werte 0x01FF oder 0x0000 lesen.&lt;br /&gt;
&lt;br /&gt;
Dies ist auch ein Grund, weshalb für Programmierung auf derart &amp;quot;niedriger&amp;quot; Ebene Kenntnisse in Prozessorarchitektur und Assembler-Programmierung sehr hilfreich sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abhilfe&#039;&#039;&#039;: Wenn man sich nicht wirklich ganz sicher ist, sollten um kritische Aktivitäten herum jedesmal die Interrupts abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel ([[AVR-GCC]]):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  cli(); // Interrupts abschalten&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
  sei(); // Interrupts wieder einschalten&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man ein globales Einschalten der Interrupts mit sei() vermeiden will, kann man die folgende Methode benutzen. Hierbei werden die Interrupts nur eingeschaltet, wenn sie vorher bereits eingeschaltet waren (Hinweis aus der FAQ von avr-libc):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t sreg; // Lokale Sicherungskopie von SREG&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
  } &lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Prozessor kann man das Problem manchmal auch ohne Abschalten von Interrupts durch geeignete Programmierung lösen. So führt&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port = (port &amp;amp; ~0x0F) | lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
immer zum beschriebenen Problem,&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port &amp;amp;= ~0x0F;&lt;br /&gt;
  port |= lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
jedoch nicht, wenn die beiden Zeilen zu jeweils einem Assembler-Befehl übersetzt werden. Was dann aber abhängig von den Optimierungs-Einstellungen des Compilers werden kann. Eine Interrupt-feste Variante für AVR-Prozessoren der neuesten Generation, wie beispielsweise Tiny2313 und Mega88 (alle ab 2004):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  PINx = (PORTx &amp;amp; 0x0F) ^ lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reentrante Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine Funktion ist reentrant (wiedereintrittsfest), wenn sie mehrmals gleichzeitig aktiv sein kann, ohne dass sich diese Aufrufe gegenseitig beeinflussen. Betrifft beispielsweise Funktionen, die sowohl im Hauptprogramm als auch in Interrupts aufgerufen werden. Manche C Compiler erfordern eine besondere Kennzeichnung solcher Funktionen. Wenn möglich sollte man es jedoch vermeiden, eine Funktion aus dem Hauptprogramm &#039;&#039;&#039;und&#039;&#039;&#039; aus einem Interrupt aus aufzurufen. Das ist meist problemlos machbar.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
* [[AVR-Tutorial: Interrupts | AVR-Tutorial - Interrupts in Assembler]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts | AVR-GCC-Tutorial - Interrupts in C]]&lt;br /&gt;
* [[ADC]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25181</id>
		<title>Interrupt</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Interrupt&amp;diff=25181"/>
		<updated>2007-12-22T13:13:06Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Formatierung geändert, Tipfehler korrigiert, UART DEMO korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Bei bestimmten Ereignissen in Prozessoren wird ein &#039;&#039;&#039;Interrupt&#039;&#039;&#039; ausgelöst. Dabei wird das Programm unterbrochen und ein Unterprogramm, die sogenannte &#039;&#039;&#039;I&#039;&#039;&#039;nterrupt &#039;&#039;&#039;S&#039;&#039;&#039;ervice &#039;&#039;&#039;R&#039;&#039;&#039;outine (&#039;&#039;&#039;ISR&#039;&#039;&#039;), aufgerufen. Wenn dieses beendet ist läuft das Hauptprogramm ganz normal weiter.&lt;br /&gt;
&lt;br /&gt;
Bei [[Mikrocontroller]]n werden Interrupts z.B. ausgelöst wenn:&lt;br /&gt;
* sich der an einem bestimmten Eingangs-Pin anliegende Pegel ändert&lt;br /&gt;
* eine vorher festgelegte Zeitspanne abgelaufen ist ([[Timer]])&lt;br /&gt;
* eine [[Seriell|serielle]] Übertragung abgeschlossen ist&lt;br /&gt;
* eine Messung des [[ADC |Analog-Digital-Wandlers]] abgeschlossen ist&lt;br /&gt;
&lt;br /&gt;
== Wichtige Eigenschaften von ISRs ==&lt;br /&gt;
&lt;br /&gt;
ISRs reagieren auf ein bestimmtes Ereignis, welches relativ oft oder selten passiert. Prinzipiell sollte man ISRs möglichst kurz halten und schnell beenden. &#039;&#039;&#039;Im Mittel muss die Interruptroutine kürzer sein als die Periodendauer des Ereignises, anderenfalls wird es passieren, dass Interrupts &amp;quot;verschluckt&amp;quot; werden&#039;&#039;&#039;, d.h. beim UART gehen Daten verloren, beim Timer gehen Zählzyklen verloren, beim AD-Wandler gehen Daten verloren etc. &#039;&#039;&#039;Solche verschluckten Interrupts sind bisweilen schwer zu finden, weil es bisweilen nur sehr wenige in ganz bestimmten Konstellationen sind.&#039;&#039;&#039; Wenn dann eine per Timer realisierte Uhr in der Stunde um 1s falsch geht, merkt man das oft nicht. &#039;&#039;&#039;Langwierige Berechnungen, Auswertungen, Ausgaben oder gar Warteschleifen haben in ISRs nichts zu suchen.&#039;&#039;&#039; Typische C-Funktionen wie printf(), scanf(), längere Ausgaben auf ein [[LCD]] etc. sollte man nicht in ISRs vornehmen. Hier kommt sinnvollerweise eine andere Programmiertechnik zu Einsatz, nämlich die Übergabe von Parametern bzw. Steuersignalen an das Hauptprogramm.&lt;br /&gt;
&lt;br /&gt;
=== Verschachtelte Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Einige Mikrocontroller, wie z.B. der [[AVR]] kennen nur zwei CPU-Zustände. Normale Programmausführung und Interruptausführung, gesteuert durch das I-Bit der CPU. Die normale Programmausführung kann jederzeit durch Interrupts unterbrochen werden. Die Interruptausführung kann nicht durch neue Interrupts unterbrochen werden. Die ISR wird erst zu Ende bearbeitet, zurück in die normale Programmausführung gesprungen und erst dann werden neue, wartende (engl. pending) Interrupts bearbeitet.&lt;br /&gt;
&lt;br /&gt;
Etwas komplexere Mikrocontroller oder grosse Prozessoren bieten verschiedene Interruptlevel (Stufen) an . Dabei gilt meist je niedriger die Zahl des Levels, um so höher die Priorität. Ein Interrupt mit höherer Priorität kann einen Interrupt mit niedriger Priorität unterbrechen. Ein Interrupt mit gleicher Priorität wie der gerade bearbeitete Interrupt kann das im allgemeinen nicht. Das nennt man verschachtelte Interrupts (engl. nested interrupts). Klassische Vertreter hierfür sind [[8051]], PowerPC, [[X86]] und Motorola 68000.&lt;br /&gt;
&lt;br /&gt;
Auf dem AVR kann man [[AVR-GCC-Tutorial#Unterbrechbare_Interruptroutinen | verschachtelte Interrupts]] sowohl in Assembler als auch in C nachbilden, allerdings mit einigen Einschränkungen und Tücken. Das ist jedoch Leuten vorbehalten, die schon viel Erfahrung auf diesem Gebiet haben. Zu 99,9% braucht man sie nicht.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten eines Timerinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein Timerinterrupt wird im allgemeinen dazu genutzt, in konstanten, periodischen Abständen bestimmte Funktionen aufzurufen. Es ist möglich, dass während eines Timerinterrupts derselbe Interrupt wieder aktiv wird, weil die Routine sehr verzweigt ist und dieses Mal sehr lange dauert.&lt;br /&gt;
&lt;br /&gt;
Wenn zum Beispiel der Timerinterrupt mit einer Periodendauer von 100ms aufgerufen wird, er aber unter bestimmten Umständen 180ms benötigt, dann wird nach 100ms nach Eintritt in die ISR der Interrupt wieder aktiv, das Timer Interrupt Flag wird gesetzt. Da aber gerade ein Interrupt bearbeitet wird, wird er nicht sofort angesprungen, weil währenddessen die Interruptfunktion global gesperrt ist (beim AVR ist das I-Bit in der CPU gelöscht). Der Interrupt wird zu Ende bearbeitet, die CPU springt zurück zum Hauptprogramm. Dabei werden die Interrupts wieder global eingeschaltet. Der zwischenzeitlich eingetroffene und zwischengespeicherte Interrupt wird nun sofort ausgeführt, sodass das Hauptprogramm praktisch gar nicht weiter kommt, bestenfalls einen Maschinenbefehl. Nun sind aber nur noch 20ms bis zum nächsten Timerinterrupt übrig. Wenn dieser nun wieder 180 ms benötigt werden in dieser Zeit aber &#039;&#039;&#039;zwei&#039;&#039;&#039; Interrupts ausgelöst, nach 20ms und 120ms. Da diese aber nicht gezählt oder andersweitig einzeln gespeichert werden können, geht ein Interrupt verloren. Das ist ein Programmfehler.&lt;br /&gt;
&lt;br /&gt;
=== Zeitverhalten des UART Empfangsinterrupts ===&lt;br /&gt;
&lt;br /&gt;
Ein [[UART]] Interrupt zum Empfang von Daten per [[RS232]] mit 115200 [[Baud]] ist ein recht häufiges Ereignis (1 Zeichen = 10 Bits = 86,8&amp;amp;mu;s). Wenn kontinuierlich Daten empfangen werden, wird nach jeweils 86,8&amp;amp;mu;s ein neuer Interrupt ausgelöst. Dabei wird das empfangene Datenbyte vom UART aus dem Empfangsschiebegregister in einem Puffer kopiert. Während das neue Zeichen Bit für Bit empfangen wird, wird es zunächst im Schieberegister des UART gespeichert. Die Daten im Puffer bleiben davon unberührt. Die CPU muss nun schnell das empfangene Datenbyte aus dem Empfangsbuffer auslesen. Die maximale Verzögerung, die sich die CPU erlauben kann von der Aktivierung des Interrupts bis zum tatsächlichen Auslesen des Datenregisters beträgt ziemlich genau die Übertragungszeit von einem Zeichen. Wenn bis dahin nicht das Zeichen von der CPU ausgelesen wurde, wird es vom UART überschrieben und ein Fehler im Statusregister des UART signalisiert (Overrun, Überlauf des Datenpuffers). Die UARTs in heutigen Mikrocontrollern haben mindestens ein Byte Puffer wie hier beschrieben. Die neueren [[AVR]]s haben sogar effektiv 3 Byte Puffer im UART, praktisch ein kleines [[FIFO]], womit der Datenempfang besser gepuffert werden kann, wenn die CPU gerade mit anderen sehr wichtigen Dingen beschäftigt ist. D.h. kurzzeitig kann sich die CPU erlauben, die Übertragungszeit von bis zu drei Zeichen zu warten, ehe sie die Daten ausliest. Dann müssen sie aber sehr schnell hintereinander gelesen werden. Im Mittel hat die CPU aber nur die Übertragungszeit eines Zeichens zur Verfügung, um es abzuholen.&lt;br /&gt;
&lt;br /&gt;
=== Zusammenfassung ===&lt;br /&gt;
&lt;br /&gt;
Interruptserviceroutinen:&lt;br /&gt;
* sollten so kurz wie möglich gehalten werden&lt;br /&gt;
* können im Einzelfall nahezu doppelt so lange dauern wie die kürzeste Periodendauer des Ereignisses, ohne dass Interrupts verloren gehen (z.B. Timerinterrupt).&lt;br /&gt;
* dürfen im Mittel maximal solange dauern wie die kürzeste Periodendauer des Ereignisses&lt;br /&gt;
* dürfen maximal solange dauern, wie die kürzeste Periodendauer des Ereignisses, wenn man auf Nummer sicher gehen will, dass keine Interrupts verschluckt werden&lt;br /&gt;
* Die Interruptzeit versteht sich immer abzüglich einer kleinen Reserve für das Anspringen und Verlassen des Interrupt minus Panikreserve&lt;br /&gt;
&lt;br /&gt;
== Beispiele für die praktische Programmierung ==&lt;br /&gt;
&lt;br /&gt;
Die Beispiele sind mit WINAVR 20060421 compiliert und getestet worden. Als Mikrocontroller wird ein AVR vom Typ ATmega32 verwendet. Alle Programme wurden mit Optimierungsstufe -Os compiliert.&lt;br /&gt;
&lt;br /&gt;
=== Steuersignale zwischen ISR und Hauptprogramm ===&lt;br /&gt;
&lt;br /&gt;
In vielen Anwendungen wird ein Timer verwendet, um in regelmässigen Abständen bestimmte Aktionen auszuführen, wie z.B. Tasten abfragen, ADC-auslesen, ein LCD auffrischen etc. Wenn viele Dinge zu erledigen sind, nebenbei aber noch andere Interrupts verwendet werden, dann ist es notwendig die Funktionsaufrufe aus dem Timerinterrupt in die Hauptschleife zu verlagern. Der Interrupt signalisiert über eine Steuervariable (engl. Flag, Flagge), das ein neuer Timerzyklus begonnen hat. Dadurch wird der Timerinterrupt sehr kurz und die langwierigen, aber meist nicht zeitkritischen Funktionen werden als normales Programm ausgeführt. Damit kann die CPU auf andere Interrupts schnell reagieren.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist auf jeden Fall, dass die Steuervariable, welche in der ISR und in der Hauptschleife verwendet wird, mit [http://www.netrino.com/Embedded-Systems/How-To/C-Volatile-Keyword &#039;&#039;&#039;volatile&#039;&#039;&#039;] deklariert wird. Ausserdem muss sowohl der Lese- als auch Schreibzugriff auf die Steuersignale [[#Atomarer Datenzugriff | &#039;&#039;&#039;atomar&#039;&#039;&#039;]] sein. Auf dem AVR ist das mit 8 Bit Variablen direkt möglich, für grössere Variablen müssen die Interrupts kurzzeitig gesperrt werden.&lt;br /&gt;
&lt;br /&gt;
Das Beispiel ist sehr einfach gehalten um das Prizip zu veranschaulichen. Ein Timer mit einer Überlaufperiodendauer von ca. 65ms stösst periodisch eine Funktion zum Togglen einer LED an, welche dadurch mit ca. 7 Hz blinkt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* Timer Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit internem 1 MHz Oszillator&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xE1&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
#define F_CPU 1000000&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen&lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t flag;&lt;br /&gt;
 &lt;br /&gt;
int main() {&lt;br /&gt;
 &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// Timer2 konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    TCCR2  = 6;                     // Vorteiler 256 -&amp;gt; ~65ms Überlaufperiode&lt;br /&gt;
    TIMSK |= (1&amp;lt;&amp;lt;TOIE2);            // Timer Overflow Interrupt freischalten &lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
 &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
        if (flag == 1) { // Neuer Timerzyklus ?&lt;br /&gt;
            flag = 0;&lt;br /&gt;
 &lt;br /&gt;
            // hier steht jetzt in Normalfall ein grosser Programmblock ;-) &lt;br /&gt;
            PORTD ^= (1 &amp;lt;&amp;lt; PD5);    // LED toggeln&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Timer2 overflow Interrupt&lt;br /&gt;
// hier wird der Hauptschleife ein neuer Timerinterrupt signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR( TIMER2_OVF_vect ) {&lt;br /&gt;
    flag = 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== UART mit Interrupts ===&lt;br /&gt;
&lt;br /&gt;
Der UART ist ein oft benutztes Modul eines Mikrocontrollers. Anfänger nutzen ihn meist im sogenannten Polling Betrieb (engl. to poll, abfragen). D.h. wenn ein Zeichen empfangen werden soll, fragt eine Funktion den UART in einer Schleife ununterbrochen ab, ob Daten empfangen wurden. In dieser Zeit macht die CPU nichts anderes! Und wenn lange kein Zeichen eintrifft tut sie sehr lange nichts, sie ist praktisch blockiert! Senden verläuft ähnlich, nur dass hier die CPU vor dem Senden prüft, ob der UART ein neues Byte aufnehmen kann. D.h. während der UART selbsttätig das Zeichen sendet ist die CPU zum Warten verdammt. All diese Nachteile haben nur einen Vorteil. Die Funktionen und Mechanismen zur UART-Nutzung sind sehr einfach, klein und leicht anwendbar.&lt;br /&gt;
&lt;br /&gt;
Will man aber die CPU nicht sinnlos warten lassen, was vor allem bei niedrigeren Baudraten ziemlich lange sein kann, muss man die Interrupts nutzen. Der AVR hat gleich drei davon.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RXC&#039;&#039;&#039; (Receive Complete): Ein Zeichen wurde empfangen.&lt;br /&gt;
* &#039;&#039;&#039;UDRE&#039;&#039;&#039; (UART Data Register Empty): Der Zwischenpuffer des Senders ist leer und kann ein neues Zeichen aufnehmen. Dieser Zwischenpuffer ist wichtig, um lückenlos auch bei hohen Baudraten senden zu können.&lt;br /&gt;
* &#039;&#039;&#039;TXC&#039;&#039;&#039; (Transmit Complete): Das aktuelle Zeichen wurde vollständig inclusive Stopbit gesendet und es liegt kein neues Datenbyte im Sendepuffer. Dieser Interrupt ist extrem nützlich für eine Halbduplexkommunikation, z.B. auf einem RS485-[[Bus]]. Hier kann man nach dem vollständigen Senden aller Bytes den Bustranceiver (z.B. MAX485) von Senden auf Empfangen umschalten, um den Bus freizugeben.&lt;br /&gt;
&lt;br /&gt;
Bei Nutzung der Interrupts kann die CPU andere Dinge bearbeiten und muss nur kurz einen Interrupt ausführen, wenn ein Zeichen empfangen oder gesendet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Kommunikation zwischen ISRs und Hauptschleife erfolgt wieder durch Flags und zwei Pufferarrays (uart_rx_buffer und uart_tx_buffer). Es gibt zwei Funktionen, eine zum Senden von Strings, eine zum Empfangen. Das Senden sowie Empfangen kann parallel erfolgen und läuft vollkommen unabhängig vom Hauptprogramm. Die Daten werden in spezielle Puffer kopiert, sodass das Hauptprogramm mit seinen Strings sofort weiterarbeiten kann. Im Beispiel ist die CPU nicht wirklich mit sinnvollen Dingen beschäftigt, zur Demonstration des Prinzips aber ausreichend.&lt;br /&gt;
&lt;br /&gt;
Um das das Programm real zu nutzen braucht man ein Terminalprogramm, z.B. Hyperterminal von Windows. Dort muss nur die richtige Baudrate eingestellt werden (9600 8N1, 9600 Baud, 8 Bits, keine Parität, 1 Stopbit, keine Flusskontrolle). Ausserdem muss man im Menu Datei -&amp;gt; Eigenschaften -&amp;gt; Einstellungen -&amp;gt; ASCII Konfiguration den Punkt &amp;quot;Eingegebene Zeichen lokal ausgeben (lokales Echo)&amp;quot; aktivieren. Nun kann man beliebige Texte eintippen. Mit RETURN wird die Eingabe abgeschlossen und der AVR vermittelt den empfangenen String an das Hauptprogramm. Diese sendet ihn einfach zurück, paralle dazu wird der String gemorst per LED angezeigt. Sollte es Probleme bei der Inbetriebnahme des UART geben, so findet man hier wichtige Hinweise zur [[AVR_Checkliste#UART.2FUSART | Fehlersuche]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*&lt;br /&gt;
* UART Interrupt Demo&lt;br /&gt;
*&lt;br /&gt;
* ATmega32 mit 3,6864 MHz Quarz an XTAL1/XTAL2&lt;br /&gt;
*&lt;br /&gt;
* LOW Fuse Byte = 0xFF&lt;br /&gt;
*&lt;br /&gt;
* An PD5 muss eine LED mit 1 kOhm Vorwiderstand angeschlossen werden&lt;br /&gt;
* An PD0/PD1 ist ein MAX232 angeschlosssen, um Daten vom PC zu empfangen/senden&lt;br /&gt;
*&lt;br /&gt;
*******************************************************************************&lt;br /&gt;
*/&lt;br /&gt;
 &lt;br /&gt;
// Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define F_CPU 3686400L               &lt;br /&gt;
// &amp;quot;Morsedauer&amp;quot; für ein Bit in Millisekunden&lt;br /&gt;
#define BITZEIT 100     &lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
// Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!&lt;br /&gt;
#define BAUD 9600L          &lt;br /&gt;
 &lt;br /&gt;
// Berechnungen&lt;br /&gt;
// clever runden&lt;br /&gt;
#define UBRR_VAL  ((F_CPU+BAUD*8)/(BAUD*16)-1)  &lt;br /&gt;
// Reale Baudrate&lt;br /&gt;
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     &lt;br /&gt;
// Fehler in Promille &lt;br /&gt;
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) &lt;br /&gt;
 &lt;br /&gt;
#if ((BAUD_ERROR&amp;gt;10) || (BAUD_ERROR&amp;lt;-10))&lt;br /&gt;
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! &lt;br /&gt;
#endif&lt;br /&gt;
 &lt;br /&gt;
// globale Variablen für den UART&lt;br /&gt;
 &lt;br /&gt;
// Puffergrösse in Bytes, RX und TX sind gleich gross&lt;br /&gt;
#define uart_buffer_size 32             &lt;br /&gt;
 &lt;br /&gt;
volatile uint8_t uart_rx_flag=0;            // Flag, String komplett empfangen&lt;br /&gt;
volatile uint8_t uart_tx_flag=1;            // Flag, String komplett gesendet&lt;br /&gt;
char uart_rx_buffer[uart_buffer_size];      // Empfangspuffer&lt;br /&gt;
char uart_tx_buffer[uart_buffer_size];      // Sendepuffer&lt;br /&gt;
 &lt;br /&gt;
// lange, variable Wartezeit, Einheit in Millisekunden&lt;br /&gt;
void long_delay(uint16_t ms) {&lt;br /&gt;
    for (; ms&amp;gt;0; ms--) _delay_ms(1);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen String senden&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_t_flag==1 ist&lt;br /&gt;
// nur dann kann ein neuer String gesendet werden&lt;br /&gt;
 &lt;br /&gt;
void put_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_tx_flag==1) {&lt;br /&gt;
      // String daten ind en Sendepuffer kopieren&lt;br /&gt;
      strcpy(uart_tx_buffer, daten);      &lt;br /&gt;
      // Flag für &#039;Senden ist komplett&#039; löschen, &lt;br /&gt;
      uart_tx_flag = 0;                    &lt;br /&gt;
      // UDRE Interrupt einschalten, los gehts&lt;br /&gt;
      UCSRB |= (1&amp;lt;&amp;lt;UDRIE); &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// einen empfangenen String kopieren&lt;br /&gt;
// vor Aufruf der Funktion muss man prüfen, ob uart_rx_flag==1 ist&lt;br /&gt;
// anderenfalls ist der RX Buffer noch ungültig&lt;br /&gt;
 &lt;br /&gt;
void get_string(char *daten) {&lt;br /&gt;
 &lt;br /&gt;
   if (uart_rx_flag==1) {&lt;br /&gt;
      // String kopieren&lt;br /&gt;
      strcpy(daten, uart_rx_buffer);      &lt;br /&gt;
      // Flag löschen&lt;br /&gt;
      uart_rx_flag = 0;                    &lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Ein Byte im RS232 Format auf eine LED ausgeben&lt;br /&gt;
 &lt;br /&gt;
void morse(uint8_t data) {&lt;br /&gt;
    uint8_t i;&lt;br /&gt;
 &lt;br /&gt;
    // Startbit, immer 0&lt;br /&gt;
    PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);           // LED aus&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
 &lt;br /&gt;
    for(i=0; i&amp;lt;8; i++) {&lt;br /&gt;
        if (data &amp;amp; 0x01)            // Prüfe Bit #0&lt;br /&gt;
            PORTD |= (1 &amp;lt;&amp;lt; PD5);    // LED an&lt;br /&gt;
        else&lt;br /&gt;
            PORTD &amp;amp;= ~(1 &amp;lt;&amp;lt; PD5);   // LED aus      &lt;br /&gt;
        long_delay(BITZEIT);        &lt;br /&gt;
        data &amp;gt;&amp;gt;= 1;                 // nächstes Bit auf Bit #0 schieben&lt;br /&gt;
    }&lt;br /&gt;
    // Stopbit, immer 1&lt;br /&gt;
    PORTD |= (1 &amp;lt;&amp;lt; PD5);            // LED an&lt;br /&gt;
    long_delay(BITZEIT);&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// Hauptprogramm&lt;br /&gt;
 &lt;br /&gt;
int main (void) {&lt;br /&gt;
 &lt;br /&gt;
    char stringbuffer[64];  // Allgemeiner Puffer für Strings&lt;br /&gt;
    uint8_t buffer_full=0;  // noch ein Flag, aber nur in der Hauptschleife&lt;br /&gt;
    char * charpointer;     // Hilfszeiger&lt;br /&gt;
    &lt;br /&gt;
// IO konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    DDRA = 0xFF;&lt;br /&gt;
    DDRB = 0xFF;&lt;br /&gt;
    DDRC = 0xFF;&lt;br /&gt;
    DDRD = 0xFF;&lt;br /&gt;
 &lt;br /&gt;
// UART konfigurieren&lt;br /&gt;
 &lt;br /&gt;
    UBRRH = UBRR_VAL &amp;gt;&amp;gt; 8;&lt;br /&gt;
    UBRRL = UBRR_VAL &amp;amp; 0xFF;&lt;br /&gt;
    UCSRB = (1&amp;lt;&amp;lt;RXCIE) | (1&amp;lt;&amp;lt;RXEN) | (1&amp;lt;&amp;lt;TXEN); &lt;br /&gt;
 &lt;br /&gt;
// Stringpuffer initialisieren&lt;br /&gt;
 &lt;br /&gt;
    stringbuffer[0] = &#039;\n&#039;;&lt;br /&gt;
    stringbuffer[1] = &#039;\r&#039;;&lt;br /&gt;
 &lt;br /&gt;
// Interrupts freigeben&lt;br /&gt;
 &lt;br /&gt;
    sei();&lt;br /&gt;
    &lt;br /&gt;
// Endlose Hauptschleife&lt;br /&gt;
 &lt;br /&gt;
    while(1) {&lt;br /&gt;
 &lt;br /&gt;
        // &amp;quot;Sinnvolle&amp;quot; CPU Tätigkeit &lt;br /&gt;
        PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
        PORTD |= (1&amp;lt;&amp;lt;PD5);&lt;br /&gt;
        long_delay(300);&lt;br /&gt;
    &lt;br /&gt;
        // Wurde ein kompletter String empfangen &lt;br /&gt;
        // und der Buffer ist leer?&lt;br /&gt;
        if (uart_rx_flag==1 &amp;amp;&amp;amp; buffer_full==0) {    &lt;br /&gt;
            // ja, dann String lesen, &lt;br /&gt;
            // die ersten zwei Zeichen &lt;br /&gt;
            // aber nicht überschreiben&lt;br /&gt;
            get_string(stringbuffer+2);             &lt;br /&gt;
            buffer_full=1;&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        // Ist letzte Stringsendung abgeschlossen &lt;br /&gt;
        // und ein neuer String verfügbar?&lt;br /&gt;
        if (uart_tx_flag==1 &amp;amp;&amp;amp; buffer_full==1) {    &lt;br /&gt;
            // Newline + Carrige return anfügen&lt;br /&gt;
            strcat(stringbuffer, &amp;quot;\n\r&amp;quot;);           &lt;br /&gt;
            put_string(stringbuffer); // zurücksenden&lt;br /&gt;
            buffer_full=0; // Buffer ist wieder verfügbar&lt;br /&gt;
            // Alle Zeichen per LED morsen&lt;br /&gt;
            charpointer = stringbuffer;&lt;br /&gt;
            while(*charpointer) morse(*charpointer++);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART RX complete interrupt&lt;br /&gt;
 &lt;br /&gt;
// hier werden Daten vom PC empfangen und in einem String zwischengespeichert&lt;br /&gt;
// Wird ein Stringterminator empfangen, wird ein Flag gesetzt, welches dem &lt;br /&gt;
// Hauptprogramm den kompletten Empfang signalisiert&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_RXC_vect) {&lt;br /&gt;
    &lt;br /&gt;
    static uint8_t uart_rx_cnt;     // Zähler für empfangene Zeichen&lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // Daten auslesen, dadurch wird das Interruptflag gelöscht              &lt;br /&gt;
    data = UDR;&lt;br /&gt;
    &lt;br /&gt;
    // Ist Puffer frei für neue Daten? &lt;br /&gt;
    if (!uart_rx_flag) {&lt;br /&gt;
        // ja, ist Ende des Strings (RETURN) erreicht?&lt;br /&gt;
        if (data==&#039;\r&#039;) {&lt;br /&gt;
            // ja, dann String terminieren&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=0;              &lt;br /&gt;
            // Flag für &#039;Empfangspuffer voll&#039; setzen&lt;br /&gt;
            uart_rx_flag=1;&lt;br /&gt;
            // Zähler zurücksetzen&lt;br /&gt;
            uart_rx_cnt=0;&lt;br /&gt;
        }&lt;br /&gt;
        else if (uart_rx_cnt&amp;lt;(uart_buffer_size-1)) {     &lt;br /&gt;
            // Daten in Puffer speichern&lt;br /&gt;
            // aber durch if() Pufferüberlauf vermeiden&lt;br /&gt;
            uart_rx_buffer[uart_rx_cnt]=data;          &lt;br /&gt;
            uart_rx_cnt++; // Zähler erhöhen&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
// UART TX data register empty interrupt&lt;br /&gt;
// hier werden neue Daten in das UART Senderegister geladen&lt;br /&gt;
 &lt;br /&gt;
ISR(USART_UDRE_vect) {&lt;br /&gt;
    // Zeiger auf Sendepuffer&lt;br /&gt;
    static char* uart_tx_p = uart_tx_buffer;    &lt;br /&gt;
    uint8_t data;&lt;br /&gt;
 &lt;br /&gt;
    // zu sendendes Zeichen lesen, &lt;br /&gt;
    // Zeiger auf Sendepuffer erhöhen&lt;br /&gt;
    data = *uart_tx_p++;&lt;br /&gt;
    &lt;br /&gt;
    // Ende des nullterminierten Strings erreicht?&lt;br /&gt;
    if (data==0 ) {        &lt;br /&gt;
        UCSRB &amp;amp;= ~(1&amp;lt;&amp;lt;UDRIE);       // ja, dann UDRE Interrupt ausschalten        &lt;br /&gt;
        uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen&lt;br /&gt;
        uart_tx_flag = 1;           // Flag setzen, Übertragung beeendet&lt;br /&gt;
    }&lt;br /&gt;
    else UDR = data;                // nein, Daten senden&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie lange dauert meine Interruptroutine ? ===&lt;br /&gt;
&lt;br /&gt;
Diese Frage sollte man beantworten können, zumindest sollte eine Worst Case Abschätzung gemacht werden. Das geht auf zwei Wegen.&lt;br /&gt;
&lt;br /&gt;
* Simulation, dabei muss in einer verzweigten ISR der längste Pfad simuliert werden. Dazu müssen alle beteiligten Variablen auf den ensprechenden Wert gesetzt werden.&lt;br /&gt;
* Messung mit dem [[Oszilloskop]], dabei wird zum Beginn der ISR ein Pin auf HIGH gesetzt und am Ende auf LOW. Damit kann man in Echtzeit die Dauer der ISR messen. Die zusätzlichen Taktzyklen zum Aufruf und verlassen der ISR sind konstant und im wesentlichen bekannt. Mit einem modernen Digitaloszilloskop und dem &amp;quot;Infinite Persistence Mode&amp;quot; kann man eine Worst Case Messung vornehmen&lt;br /&gt;
&lt;br /&gt;
Als Hilfmittel zur Fehlersuche kann man auch am Ende der ISR prüfen, ob das jeweilige Interrupt-Request Bit schon wieder gesetzt ist. Wenn ja, dann ist die ISR in den meisten Fällen zu lang. Auch hier kann man einen Ausgang auf HIGH setzen und somit den Fehler anzeigen.&lt;br /&gt;
&lt;br /&gt;
==Interruptfeste Programmierung==&lt;br /&gt;
&lt;br /&gt;
=== Atomarer Datenzugriff ===&lt;br /&gt;
&lt;br /&gt;
Von einem atomaren (engl. atomic) Datenzugriff spricht man, wenn der Zugriff innerhalb einer nicht unterbrechbaren Maschinenanweisung abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Alle Variablen, Steuerregister und I/O-Ports, die sowohl im Hauptprogramm als auch in Interrupts verwendet werden, sind mit viel Sorgfalt zu behandeln.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
übersetzt sich auf AVR Prozessoren in&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  IN  r16,port&lt;br /&gt;
  ORI r16,0x03&lt;br /&gt;
  OUT port,r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
Wenn nun zwischen IN und OUT ein Interrupt auftritt, der beispielsweise Bit 7 verändert, dann geht mit dem OUT-Befehl diese Änderung verloren, da der OUT Befehl den alten Zustand vor dem Interrupt wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Gefährlich ist das insbesondere deshalb, weil der Fall nur selten auftritt und dies Verhalten sehr schlecht reproduzierbar ist.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bei verschiedenen Prozessor-Architekturen tritt das Problem verschieden häufig auf. So übersetzt sich obiger Code bei MSP430 Prozessoren in einen einzelnen Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  OR  #0x03,port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
und stellt somit kein Problem dar. Im Zweifel hilft nur ein Blick in den erzeugten Assembler-Code. Bei der Übernahme fremden Codes ist dies zu beachten. Was bei i51 kein Problem war, kann bei AVR zu einem Problem werden, unter Umständen sogar abhängig von verwendeten Port sein.&lt;br /&gt;
&lt;br /&gt;
Ein ähnliches Problem entsteht bei Variablen, deren Grösse die Wortbreite der Maschine übersteigt. Bei 8-Bit-Prozessoren wie AVR oder i51 also bereits bei normalen &amp;quot;int&amp;quot; Variablen. Diese Variablen werden zwangsläufig byteweise verarbeitet. Wenn genau dazwischen ein Interrupt erfolgt, wird ein falscher Wert gelesen. Wenn beispielsweise eine Interrupt-Routine einen 16-Bit-Zähler verwendet und von 0x00FF auf 0x0100 hochzählt, dann kann das Hauptprogramm auch schon mal versehentlich die Werte 0x01FF oder 0x0000 lesen.&lt;br /&gt;
&lt;br /&gt;
Dies ist auch ein Grund, weshalb für Programmierung auf derart &amp;quot;niedriger&amp;quot; Ebene Kenntnisse in Prozessorarchitektur und Assembler-Programmierung sehr hilfreich sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abhilfe&#039;&#039;&#039;: Wenn man sich nicht wirklich ganz sicher ist, sollten um kritische Aktivitäten herum jedesmal die Interrupts abgeschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel ([[AVR-GCC]]):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  cli(); // Interrupts abschalten&lt;br /&gt;
  port |= 0x03;&lt;br /&gt;
  sei(); // Interrupts wieder einschalten&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man ein globales Einschalten der Interrupts mit sei() vermeiden will, kann man die folgende Methode benutzen. Hierbei werden die Interrupts nur eingeschaltet, wenn sie vorher bereits eingeschaltet waren (Hinweis aus der FAQ von avr-libc):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  // ...&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t sreg; // Lokale Sicherungskopie von SREG&lt;br /&gt;
    sreg = SREG;&lt;br /&gt;
    cli();&lt;br /&gt;
    // hierhin kommt der Code mit atomarem Zugriff&lt;br /&gt;
    SREG = sreg;&lt;br /&gt;
  } &lt;br /&gt;
  // ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Prozessor kann man das Problem manchmal auch ohne Abschalten von Interrupts durch geeignete Programmierung lösen. So führt&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port = (port &amp;amp; ~0x0F) | lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
immer zum beschriebenen Problem,&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  port &amp;amp;= ~0x0F;&lt;br /&gt;
  port |= lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
jedoch nicht, wenn die beiden Zeilen zu jeweils einem Assembler-Befehl übersetzt werden. Was dann aber abhängig von den Optimierungs-Einstellungen des Compilers werden kann. Eine Interrupt-feste Variante für AVR-Prozessoren der neuesten Generation, wie beispielsweise Tiny2313 und Mega88 (alle ab 2004):&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  PINx = (PORTx &amp;amp; 0x0F) ^ lcd_data;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reentrante Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine Funktion ist reentrant (wiedereintrittsfest), wenn sie mehrmals gleichzeitig aktiv sein kann, ohne dass sich diese Aufrufe gegenseitig beeinflussen. Betrifft beispielsweise Funktionen, die sowohl im Hauptprogramm als auch in Interrupts aufgerufen werden. Manche C Compiler erfordern eine besondere Kennzeichnung solcher Funktionen. Wenn möglich sollte man es jedoch vermeiden, eine Funktion aus dem Hauptprogramm &#039;&#039;&#039;und&#039;&#039;&#039; aus einem Interrupt aus aufzurufen. Das ist meist problemlos machbar.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
* [[Timer]]&lt;br /&gt;
* [[AVR-Tutorial: Interrupts | AVR-Tutorial - Interrupts in Assembler]]&lt;br /&gt;
* [[AVR-GCC-Tutorial#Programmieren_mit_Interrupts | AVR-GCC-Tutorial - Interrupts in C]]&lt;br /&gt;
* [[ADC]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Bitmanipulation&amp;diff=25167</id>
		<title>Bitmanipulation</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Bitmanipulation&amp;diff=25167"/>
		<updated>2007-12-20T16:07:42Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Bit togglen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bits setzen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Eins gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#ODER | ODER]]-Verknüpfung erreicht.  Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden auf &#039;1&#039; gesetzt. Alle Bits, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbr r16, 0b11110000     ; setzt Bits 4-7 in r16&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
ori r16, 0b11110000     ; setzt Bits 4-7 in r16, ori ist identisch mit dem Pseudobefehl sbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
sbi PORTB, 5            ; setzt Bit 5 in PortB&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..31&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von sbr als &amp;quot;setze Bit 2 und 0&amp;quot; gedeutet, während sbi sie als &amp;quot;setzte Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;sbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine ODER-Verknüpfung, während der Befehlt &#039;&#039;&#039;sbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB |= 0xF0;   // Kurzschreibweise, entspricht PORTB = PORTB | 0xF0 also bitweises ODER.&lt;br /&gt;
&lt;br /&gt;
/* übersichtlicher mittels Bit-Definitionen */&lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1&lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
PORTB |= ((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // setzt Bit 0 und 2 in PORTB auf &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben.  Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft, also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Diese Maske wird mit dem aktuellen Inhalt von PORTB bitweise ODER-verknüpft und das Ergebnis PORTB mittels &#039;|=&#039; wieder zugewiesen. Ist PORTB vorher z.B. 0b01111010, dann ist der Inhalt nach der Operation 0b01111010 &#039;&#039;or&#039;&#039; 0b00000101 = 0b01111111, die gewünschten Bits sind somit gesetzt!&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Will man das gezeigte Beispiel der Bitmanipulation auf größere Datentypen anwenden, ist zu beachten, dass der Compiler in der Operation (1 &amp;lt;&amp;lt; MEINBIT1) stillschweigend die 1 als Integer Type ansieht. Beim AVR-GCC bedeutet das 16-Bit und die folgende Operation bringt ggf. nicht das gewünschte Ergebnis. (Stichwort: &amp;quot;Integer Promotion&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Angenommen Bit 15 soll in einer 32-Bit weiten Variable gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define MEINBIT15 15&lt;br /&gt;
#define MEINBIT42 42&lt;br /&gt;
&lt;br /&gt;
uint32_t reg_32;&lt;br /&gt;
uint64_t reg_64;&lt;br /&gt;
&lt;br /&gt;
reg_32 |= (1 &amp;lt;&amp;lt; MEINBIT15);              /* FEHLER: Setzt die Bits 31 - 15, da ((int)1 &amp;lt;&amp;lt; 15) == 0xFFFF8000 */&lt;br /&gt;
&lt;br /&gt;
reg_32 |= ((uint32_t)1 &amp;lt;&amp;lt; MEINBIT15);    /* Hier wird nur Bit 15 gesetzt. */&lt;br /&gt;
reg_32 |= (1L &amp;lt;&amp;lt; MEINBIT15);             /* andere Schreibweise. */&lt;br /&gt;
reg_64 |= (1LL &amp;lt;&amp;lt; MEINBIT42);            /* Hier wird nur Bit 42 gesetzt,&lt;br /&gt;
                                            andere Schreibweise für 64 Bit (long long). */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bit löschen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Null gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#UND | UND]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;0&#039; sind, werden auf &#039;0&#039; gesetzt. Alle Bits, die in der Maske auf &#039;1&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cbr r16, 0b11110000     ; löscht Bits 0-3 in r16, setzt sie auf 0&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, 0b11110000    ; löscht Bits 0-3 in r16, andi ist identisch mit dem Pseudobefehl cbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, ~0b00001111   ; andere Schreibweise, hier wird die Bitmaske durch ~ invertiert&lt;br /&gt;
                        ; dadurch kann man einfach alle zu löschenden Bit als &#039;1&#039; angeben&lt;br /&gt;
                        ; so wie bei den Bitmasken für das setzen von Bits (positive Logik)&lt;br /&gt;
&lt;br /&gt;
cbi PORTB, 5            ; löscht Bit 5 in PortB&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..31&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt. Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von cbr als &amp;quot;lösche Bit 7,6,5,4,3 und 1&amp;quot; gedeutet, während cbi sie als &amp;quot;lösche Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;cbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine UND-Verknüpfung, während der Befehlt &#039;&#039;&#039;cbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= 0xF0;   // entspricht PORTB = PORTB &amp;amp; 0xF0 also bitweises UND;&lt;br /&gt;
                 // Bits 0-3 (das &amp;quot;niederwertige&amp;quot; Nibble) werden geloescht &lt;br /&gt;
&lt;br /&gt;
/* uebersichtlicher mittels Bit-Definitionen */	&lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1	&lt;br /&gt;
#define MEINBIT2 2	&lt;br /&gt;
&lt;br /&gt;
PORTB &amp;amp;= ~((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // löscht Bit 0 und 2 in PORTB&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Durch &#039;~&#039; wird der Wert in der Klammer invertiert, aus 0b00000101 wird 0b11111010. Schließlich wird durch &#039;&amp;amp;=&#039; mit dem aktuellen Wert von PORTB UND-verknüpft und das Ergebnis wieder PORTB zugewiesen. Ist PORTB vorher z.B. 0b01111111, dann ist der Inhalt nach der Operation 0b011111111 &#039;&#039;and&#039;&#039; 0b11111010 = 0b01111010, die gewünschten Bits (0 und 2) sind somit gelöscht. &lt;br /&gt;
&lt;br /&gt;
Die C-Ausdrücke sehen auf den ersten Blick etwas &amp;quot;erschreckend&amp;quot; aus und sind mehr &amp;quot;Tipparbeit&amp;quot;, funktionieren aber universell und sind nach einiger Gewöhnung deutlicher und nachvollziehbarer als &amp;quot;handoptimierte&amp;quot; Konstanten.&lt;br /&gt;
&lt;br /&gt;
=== Niederwertigstes gesetztes Bit löschen (Standard C) ===&lt;br /&gt;
&lt;br /&gt;
Folgender Code löscht von allen 1-Bits in einer Integer-Variable das niederwertigste, unabhängig von der Position desselben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 01101000 -&amp;gt; 01100000&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t byte;&lt;br /&gt;
&lt;br /&gt;
byte = irgendwas();&lt;br /&gt;
&lt;br /&gt;
byte = byte &amp;amp; (byte - 1); /* Diese seltsame Operation löscht das&lt;br /&gt;
                             niederwertigste 1-Bit */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann bspw. zur schnellen Paritätsgenerierung eingesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t pareven(uint8_t byte) {&lt;br /&gt;
  uint8_t par = 0;&lt;br /&gt;
&lt;br /&gt;
  while(byte) {&lt;br /&gt;
    byte = byte &amp;amp; (byte - 1);&lt;br /&gt;
    par = !par;&lt;br /&gt;
  }&lt;br /&gt;
  return par;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das genannte gilt natürlich nicht nur für 8-Bit-Integers, sondern für beliebige, vom Compiler unterstützte Wortlängen.&lt;br /&gt;
&lt;br /&gt;
== Bit togglen ==&lt;br /&gt;
&lt;br /&gt;
Togglen bedeutet den Wert eines Bits invertiert. Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] invertiert (getoggelt) werden sollen, wird die durch eine [[AVR-Tutorial:_Logik#XOR_.28Exlusives_Oder.29 | XOR]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden invertiert. Alle Bits, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR Assembler ===&lt;br /&gt;
&lt;br /&gt;
Bei [[AVR]]s erlaubt dies z.B. folgender Assemblercode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  sbic	PortB, 0    ; skip if bit is cleared&lt;br /&gt;
  rjmp	ClrBitNow   ; jump&lt;br /&gt;
  sbi	PortB, 0    ; set bit (if bit was cleared)&lt;br /&gt;
  rjmp	BitReady    ; jump&lt;br /&gt;
ClrBitNow:&lt;br /&gt;
   cbi	PortB, 0    ; clear bit (if bit was set)&lt;br /&gt;
BitReady:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kürzer gehts mit folgender Variante. Zwischen der 2. und 3. Instruktion darf jedoch kein Interrupt erfolgen, sonst würde das SBIC schon den neuen Pegel des SBI auswerten (evtl. globale Interrupts ausschalten).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 cli                 ; disable global interrupts&lt;br /&gt;
 sbis	PinB,	0    ; skip if bit is set&lt;br /&gt;
 sbi	PortB,	0    ; otherwise set bit&lt;br /&gt;
 sbic	PinB,	0    ; skip if bit is cleared&lt;br /&gt;
 cbi	PortB,	0    ; otherwise clear bit&lt;br /&gt;
 sei                 ; enable global interrupts&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch kürzer gehts so:&amp;lt;br&amp;gt;&lt;br /&gt;
Die zweite Zeile mit dem Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; lädt die Bitmaske, in welcher die zu  den zu toggelnden Bits auf &#039;1&#039; gesetzt sind. In diesem Beispiel wird das dritte Bit invertiert. Der Vorteil dieser Methode ist neben der Kürze und Übersichtlichkeit auch die Möglichkeit, bis zu 8 Bit gleichzeitig zu toggeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in     R24, PORTE   ; Daten lesen&lt;br /&gt;
 ldi	R25, 0x04    ; Bitmaske laden, hier Bit#2&lt;br /&gt;
 eor	R24, R25     ; Exklusiv ODER&lt;br /&gt;
 out	PORTE, R24   ; Daten zurückschreiben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit gibt es, wenn man nur das 8. Bit kippen will:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in      r16, PORTB&lt;br /&gt;
 subi    r16, 0x80&lt;br /&gt;
 out     PORTB, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 PORTB ^= (1&amp;lt;&amp;lt;PB0);    /* XOR, Kurzschreibweise, PORTB = PORTB ^ (1&amp;lt;&amp;lt;PB0) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Neuere ATmegas ===&lt;br /&gt;
&lt;br /&gt;
Bei den neueren ATmegas (z.B. ATmega48) kann man IO-Pins direkt ohne den Umweg über Register togglen, indem man das entsprechende Bit im PINx-Register &#039;&#039;&#039;setzt&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbi PIND, 2       ; Bit 2 von Port D togglen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 8051er ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cpl bitadresse&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:8051]][[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25165</id>
		<title>DDS</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25165"/>
		<updated>2007-12-20T15:16:55Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Lesestoff */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Direkte Digitale Synthese===&lt;br /&gt;
Erzeugung einer Wechselspannung / eines Wechselstromes mittels Frequenzgenerator, FixedPoint-Addierer, LookUpTable und Digital-Analog-Wandler [[DAC]].&lt;br /&gt;
&lt;br /&gt;
==Funktionsweise==&lt;br /&gt;
Erzeugung der Basisfrequenz durch Quarzoszillator (evtl. mit Phase-Locked-Loop PLL Synthesizer). Ausgangsfrequenz ist immer geringer als die Basisfrequenz.&lt;br /&gt;
&lt;br /&gt;
Sehr gute Beschreibung der Theorie und der Nachteile (PhasenRauschen, Oberwellen):&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.analog.com/UploadedFiles/Tutorials/450968421DDS_Tutorial_rev12-2-99.pdf&lt;br /&gt;
&lt;br /&gt;
Die LookUpTable kann fest (meist Sinus) oder programmierbar (RAM, Arbitrary Waveform) oder ein Algorithmus sein.&lt;br /&gt;
&lt;br /&gt;
Verwendung im NF / Audio- und HF / Funk-Bereich (bis 1 GHz).&lt;br /&gt;
&lt;br /&gt;
Hersteller: Analog Devices http://www.analog.com&lt;br /&gt;
&lt;br /&gt;
Bekannte ICs: Fairchild ML2037, AD9833 (25MHz), AD9835, AD9851, AD9854 (300MHz), AD9951 (400MHz)&lt;br /&gt;
&lt;br /&gt;
Die modernener Chips beinhalten weitere Funktionen wie digitale Amplitudenmodulation (AM), Phasenmodulation (PM), Frequency Shift Keying (FSK).&lt;br /&gt;
&lt;br /&gt;
==Projekte:==&lt;br /&gt;
[[Digitaler Funktionsgenerator]]&lt;br /&gt;
&lt;br /&gt;
HF-Meßplatz&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-350237.html&lt;br /&gt;
&lt;br /&gt;
Einfache Schaltung: 6 Drehschalter - PIC16F84 - AD9833&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-403708.html&lt;br /&gt;
&lt;br /&gt;
Ganz ähnlich, mit ML2037:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.seattlerobotics.org/encoder/200205/ddsfgen.htm&lt;br /&gt;
&lt;br /&gt;
DDS-Signalgenerator bis 500 MHz (1200MHz Takt)&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.dg4rbf.de/ -&amp;gt; SYN500, SYN500/2&lt;br /&gt;
&lt;br /&gt;
=Lesestoff=&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=dds* Suche nach dds* in allen Foren]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=synthe* Suche nach synthe* in allen Foren]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#new Lange, ausführliche Diskussion zur Berechung des Frequency Tuning Words]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#710690 Praktische Umsetzung]&lt;br /&gt;
&lt;br /&gt;
Implementierung im NF-Bereich auch durch µC möglich, z.B. für DialTone (DTFM):&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.myplace.nu/avr/minidds/index.htm&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.atmel.com/dyn/resources/prod_documents/doc1982.pdf&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=sinus*&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=tonerzeug*&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25164</id>
		<title>DDS</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=DDS&amp;diff=25164"/>
		<updated>2007-12-20T15:15:06Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* Lesestoff */ Links auf Threads gesetzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Direkte Digitale Synthese===&lt;br /&gt;
Erzeugung einer Wechselspannung / eines Wechselstromes mittels Frequenzgenerator, FixedPoint-Addierer, LookUpTable und Digital-Analog-Wandler [[DAC]].&lt;br /&gt;
&lt;br /&gt;
==Funktionsweise==&lt;br /&gt;
Erzeugung der Basisfrequenz durch Quarzoszillator (evtl. mit Phase-Locked-Loop PLL Synthesizer). Ausgangsfrequenz ist immer geringer als die Basisfrequenz.&lt;br /&gt;
&lt;br /&gt;
Sehr gute Beschreibung der Theorie und der Nachteile (PhasenRauschen, Oberwellen):&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.analog.com/UploadedFiles/Tutorials/450968421DDS_Tutorial_rev12-2-99.pdf&lt;br /&gt;
&lt;br /&gt;
Die LookUpTable kann fest (meist Sinus) oder programmierbar (RAM, Arbitrary Waveform) oder ein Algorithmus sein.&lt;br /&gt;
&lt;br /&gt;
Verwendung im NF / Audio- und HF / Funk-Bereich (bis 1 GHz).&lt;br /&gt;
&lt;br /&gt;
Hersteller: Analog Devices http://www.analog.com&lt;br /&gt;
&lt;br /&gt;
Bekannte ICs: Fairchild ML2037, AD9833 (25MHz), AD9835, AD9851, AD9854 (300MHz), AD9951 (400MHz)&lt;br /&gt;
&lt;br /&gt;
Die modernener Chips beinhalten weitere Funktionen wie digitale Amplitudenmodulation (AM), Phasenmodulation (PM), Frequency Shift Keying (FSK).&lt;br /&gt;
&lt;br /&gt;
==Projekte:==&lt;br /&gt;
[[Digitaler Funktionsgenerator]]&lt;br /&gt;
&lt;br /&gt;
HF-Meßplatz&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-350237.html&lt;br /&gt;
&lt;br /&gt;
Einfache Schaltung: 6 Drehschalter - PIC16F84 - AD9833&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.mikrocontroller.net/forum/read-1-403708.html&lt;br /&gt;
&lt;br /&gt;
Ganz ähnlich, mit ML2037:&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.seattlerobotics.org/encoder/200205/ddsfgen.htm&lt;br /&gt;
&lt;br /&gt;
DDS-Signalgenerator bis 500 MHz (1200MHz Takt)&amp;lt;br&amp;gt;&lt;br /&gt;
http://www.dg4rbf.de/ -&amp;gt; SYN500, SYN500/2&lt;br /&gt;
&lt;br /&gt;
=Lesestoff=&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=dds*&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=synthe*&amp;lt;br&amp;gt;&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#new Lange, ausführliche Diskussion]&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/84498#710690 Praktische Umsetzung]&lt;br /&gt;
&lt;br /&gt;
Implementierung im NF-Bereich auch durch µC möglich, z.B. für DialTone (DTFM):&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.myplace.nu/avr/minidds/index.htm&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.atmel.com/dyn/resources/prod_documents/doc1982.pdf&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=sinus*&amp;lt;br&amp;gt;&lt;br /&gt;
* http://www.mikrocontroller.net/forum/list-1-1.html?filter=tonerzeug*&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Bitmanipulation&amp;diff=25163</id>
		<title>Bitmanipulation</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Bitmanipulation&amp;diff=25163"/>
		<updated>2007-12-20T15:03:18Z</updated>

		<summary type="html">&lt;p&gt;FalkB: Fehler beim Bitsetzen korrigiert, kleine Änderungen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Bits setzen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Eins gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#ODER | ODER]]-Verknüpfung erreicht.  Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden auf &#039;1&#039; gesetzt. Alle Bits, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbr r16, 0b11110000     ; setzt Bits 4-7 in r16&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
ori r16, 0b11110000     ; setzt Bits 4-7 in r16, ori ist identisch mit dem Pseudobefehl sbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
sbi PORTB, 5            ; setzt Bit 5 in PortB&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..31&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von sbr als &amp;quot;setze Bit 2 und 0&amp;quot; gedeutet, während sbi sie als &amp;quot;setzte Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;sbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine ODER-Verknüpfung, während der Befehlt &#039;&#039;&#039;sbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB |= 0xF0;   // Kurzschreibweise, entspricht PORTB = PORTB | 0xF0 also bitweises ODER.&lt;br /&gt;
&lt;br /&gt;
/* übersichtlicher mittels Bit-Definitionen */&lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1&lt;br /&gt;
#define MEINBIT2 2&lt;br /&gt;
&lt;br /&gt;
PORTB |= ((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // setzt Bit 0 und 2 in PORTB auf &amp;quot;1&amp;quot;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben.  Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft, also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Diese Maske wird mit dem aktuellen Inhalt von PORTB bitweise ODER-verknüpft und das Ergebnis PORTB mittels &#039;|=&#039; wieder zugewiesen. Ist PORTB vorher z.B. 0b01111010, dann ist der Inhalt nach der Operation 0b01111010 &#039;&#039;or&#039;&#039; 0b00000101 = 0b01111111, die gewünschten Bits sind somit gesetzt!&lt;br /&gt;
&lt;br /&gt;
Anmerkung: Will man das gezeigte Beispiel der Bitmanipulation auf größere Datentypen anwenden, ist zu beachten, dass der Compiler in der Operation (1 &amp;lt;&amp;lt; MEINBIT1) stillschweigend die 1 als Integer Type ansieht. Beim AVR-GCC bedeutet das 16-Bit und die folgende Operation bringt ggf. nicht das gewünschte Ergebnis. (Stichwort: &amp;quot;Integer Promotion&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Angenommen Bit 15 soll in einer 32-Bit weiten Variable gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#define MEINBIT15 15&lt;br /&gt;
#define MEINBIT42 42&lt;br /&gt;
&lt;br /&gt;
uint32_t reg_32;&lt;br /&gt;
uint64_t reg_64;&lt;br /&gt;
&lt;br /&gt;
reg_32 |= (1 &amp;lt;&amp;lt; MEINBIT15);              /* FEHLER: Setzt die Bits 31 - 15, da ((int)1 &amp;lt;&amp;lt; 15) == 0xFFFF8000 */&lt;br /&gt;
&lt;br /&gt;
reg_32 |= ((uint32_t)1 &amp;lt;&amp;lt; MEINBIT15);    /* Hier wird nur Bit 15 gesetzt. */&lt;br /&gt;
reg_32 |= (1L &amp;lt;&amp;lt; MEINBIT15);             /* andere Schreibweise. */&lt;br /&gt;
reg_64 |= (1LL &amp;lt;&amp;lt; MEINBIT42);            /* Hier wird nur Bit 42 gesetzt,&lt;br /&gt;
                                            andere Schreibweise für 64 Bit (long long). */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bit löschen ==&lt;br /&gt;
&lt;br /&gt;
Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] auf Null gesetzt werden sollen, wird dies durch eine [[AVR-Tutorial:_Logik#UND | UND]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;0&#039; sind, werden auf &#039;0&#039; gesetzt. Alle Bits, die in der Maske auf &#039;1&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR-Assembler ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cbr r16, 0b11110000     ; löscht Bits 0-3 in r16, setzt sie auf 0&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, 0b11110000    ; löscht Bits 0-3 in r16, andi ist identisch mit dem Pseudobefehl cbr&lt;br /&gt;
                        ; funktioniert nur für die Arbeitsregister r16-r31&lt;br /&gt;
&lt;br /&gt;
andi r16, ~0b00001111   ; andere Schreibweise, hier wird die Bitmaske durch ~ invertiert&lt;br /&gt;
                        ; dadurch kann man einfach alle zu löschenden Bit als &#039;1&#039; angeben&lt;br /&gt;
                        ; so wie bei den Bitmasken für das setzen von Bits (positive Logik)&lt;br /&gt;
&lt;br /&gt;
cbi PORTB, 5            ; löscht Bit 5 in PortB&lt;br /&gt;
                        ; funktioniert nur für die IO-Register 0..31&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch hier gilt. Man beachte den Unterschied! Eine &amp;quot;5&amp;quot; würde von cbr als &amp;quot;lösche Bit 7,6,5,4,3 und 1&amp;quot; gedeutet, während cbi sie als &amp;quot;lösche Bit 5&amp;quot; versteht. Der Befehl &#039;&#039;&#039;cbr&#039;&#039;&#039; erwartet ein Bit&#039;&#039;&#039;muster&#039;&#039;&#039; für eine UND-Verknüpfung, während der Befehlt &#039;&#039;&#039;cbi&#039;&#039;&#039; die Bit&#039;&#039;&#039;nummer&#039;&#039;&#039; benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= 0xF0;   // entspricht PORTB = PORTB &amp;amp; 0xF0 also bitweises UND;&lt;br /&gt;
                 // Bits 0-3 (das &amp;quot;niederwertige&amp;quot; Nibble) werden geloescht &lt;br /&gt;
&lt;br /&gt;
/* uebersichtlicher mittels Bit-Definitionen */	&lt;br /&gt;
#define MEINBIT0 0&lt;br /&gt;
#define MEINBIT1 1	&lt;br /&gt;
#define MEINBIT2 2	&lt;br /&gt;
&lt;br /&gt;
PORTB &amp;amp;= ~((1 &amp;lt;&amp;lt; MEINBIT0) | (1 &amp;lt;&amp;lt; MEINBIT2)); // löscht Bit 0 und 2 in PORTB&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die letzte Zeile &amp;quot;entschlüsselt&amp;quot;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Zuerst wird durch die &#039;&amp;lt;&amp;lt;&#039;-Ausdrücke eine &amp;quot;1&amp;quot; n-mal nach links geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000001 für (1 &amp;lt;&amp;lt; MEINBIT0) und 0b00000100 für (1 &amp;lt;&amp;lt; MEINBIT2). Das Ergebnis wird bitweise ODER-verknüpft also 0b00000001 &#039;&#039;or&#039;&#039; 0b00000100 wird zu 0b00000101. Durch &#039;~&#039; wird der Wert in der Klammer invertiert, aus 0b00000101 wird 0b11111010. Schließlich wird durch &#039;&amp;amp;=&#039; mit dem aktuellen Wert von PORTB UND-verknüpft und das Ergebnis wieder PORTB zugewiesen. Ist PORTB vorher z.B. 0b01111111, dann ist der Inhalt nach der Operation 0b011111111 &#039;&#039;and&#039;&#039; 0b11111010 = 0b01111010, die gewünschten Bits (0 und 2) sind somit gelöscht. &lt;br /&gt;
&lt;br /&gt;
Die C-Ausdrücke sehen auf den ersten Blick etwas &amp;quot;erschreckend&amp;quot; aus und sind mehr &amp;quot;Tipparbeit&amp;quot;, funktionieren aber universell und sind nach einiger Gewöhnung deutlicher und nachvollziehbarer als &amp;quot;handoptimierte&amp;quot; Konstanten.&lt;br /&gt;
&lt;br /&gt;
=== Niederwertigstes gesetztes Bit löschen (Standard C) ===&lt;br /&gt;
&lt;br /&gt;
Folgender Code löscht von allen 1-Bits in einer Integer-Variable das niederwertigste, unabhängig von der Position desselben.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 01101000 -&amp;gt; 01100000&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t byte;&lt;br /&gt;
&lt;br /&gt;
byte = irgendwas();&lt;br /&gt;
&lt;br /&gt;
byte = byte &amp;amp; (byte - 1); /* Diese seltsame Operation löscht das&lt;br /&gt;
                             niederwertigste 1-Bit */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann bspw. zur schnellen Paritätsgenerierung eingesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
uint8_t pareven(uint8_t byte) {&lt;br /&gt;
  uint8_t par = 0;&lt;br /&gt;
&lt;br /&gt;
  while(byte) {&lt;br /&gt;
    byte = byte &amp;amp; (byte - 1);&lt;br /&gt;
    par = !par;&lt;br /&gt;
  }&lt;br /&gt;
  return par;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das genannte gilt natürlich nicht nur für 8-Bit-Integers, sondern für beliebige, vom Compiler unterstützte Wortlängen.&lt;br /&gt;
&lt;br /&gt;
== Bit togglen ==&lt;br /&gt;
&lt;br /&gt;
Togglen bedeutet den Wert eines Bits invertiert. Wenn in einem Byte mehrere [[Digitaltechnik|Bits]] invertiert (getogglet) werden sollen, wird die durch eine [[AVR-Tutorial:_Logik#XOR_.28Exlusives_Oder.29 | XOR]]-Verknüpfung erreicht. Alle Bits, welche in der Bitmaske &#039;1&#039; sind, werden invertiert. Alle Bit, die in der Maske auf &#039;0&#039; gesetzt sind, bleiben unverändert.&lt;br /&gt;
&lt;br /&gt;
=== AVR Assembler ===&lt;br /&gt;
&lt;br /&gt;
Bei [[AVR]]s erlaubt dies z.B. folgender Assemblercode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
  sbic	PortB, 0    ; skip if bit is cleared&lt;br /&gt;
  rjmp	ClrBitNow   ; jump&lt;br /&gt;
  sbi	PortB, 0    ; set bit (if bit was cleared)&lt;br /&gt;
  rjmp	BitReady    ; jump&lt;br /&gt;
ClrBitNow:&lt;br /&gt;
   cbi	PortB, 0    ; clear bit (if bit was set)&lt;br /&gt;
BitReady:&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kürzer gehts mit folgender Variante. Zwischen der 2. und 3. Instruktion darf jedoch kein Interrupt erfolgen, sonst würde das SBIC schon den neuen Pegel des SBI auswerten (evtl. globale Interrupts ausschalten).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 cli                 ; disable global interrupts&lt;br /&gt;
 sbis	PinB,	0    ; skip if bit is set&lt;br /&gt;
 sbi	PortB,	0    ; otherwise set bit&lt;br /&gt;
 sbic	PinB,	0    ; skip if bit is cleared&lt;br /&gt;
 cbi	PortB,	0    ; otherwise clear bit&lt;br /&gt;
 sei                 ; enable global interrupts&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch kürzer gehts so:&amp;lt;br&amp;gt;&lt;br /&gt;
Die zweite Zeile mit dem Befehl &#039;&#039;&#039;ldi&#039;&#039;&#039; lädt die Bitmaske, in welcher die zu  den zu toggelnden Bits auf &#039;1&#039; gesetzt sind. In diesem Beispiel wird das dritte Bit invertiert. Der Vorteil dieser Methode ist neben der Kürze und Übersichtlichkeit auch die Möglichkeit, bis zu 8 Bit gleichzeitig zu toggeln.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in     R24, PORTE   ; Daten lesen&lt;br /&gt;
 ldi	R25, 0x04    ; Bitmaske laden, hier Bit#2&lt;br /&gt;
 eor	R24, R25     ; Exklusiv ODER&lt;br /&gt;
 out	PORTE, R24   ; Daten zurückschreiben&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine andere Möglichkeit gibt es, wenn man nur das 8. Bit kippen will:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
 in      r16, PORTB&lt;br /&gt;
 subi    r16, 0x80&lt;br /&gt;
 out     PORTB, r16&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standard C ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
 PORTB ^= (1&amp;lt;&amp;lt;PB0);    /* XOR, Kurzschreibweise, PORTB = PORTB ^ (1&amp;lt;&amp;lt;PB0) */&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Neuere ATmegas ===&lt;br /&gt;
&lt;br /&gt;
Bei den neueren ATmegas (z.B. ATmega48) kann man IO-Pins direkt ohne den Umweg über Register togglen, indem man das entsprechende Bit im PINx-Register &#039;&#039;&#039;setzt&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
sbi PIND, 2       ; Bit 2 von Port D togglen&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 8051er ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;avrasm&amp;gt;&lt;br /&gt;
cpl bitadresse&lt;br /&gt;
&amp;lt;/avrasm&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:8051]][[Category:AVR]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=25147</id>
		<title>Pegelwandler</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=Pegelwandler&amp;diff=25147"/>
		<updated>2007-12-19T19:32:56Z</updated>

		<summary type="html">&lt;p&gt;FalkB: /* STEP-DOWN: 5V -&amp;gt; 3.3V */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Vorwort ==&lt;br /&gt;
&lt;br /&gt;
Dies ist die erste aufgeräumte Version. Sicher nicht die letzte.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-1-375051.html &amp;quot;Stein des Anstosses&amp;quot;]&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=pegelwand*+levelsh*&amp;amp;x=0&amp;amp;y=0 Suche in den Forenartikel]&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Pegelwandeln (engl. level shifting) wird oft notwendig, wenn Systeme mit unterschiedlicher Ausgangs- und Eingangsspannungen (z.B. Versorgungs- oder Logikspannungen) miteinander verbunden werden sollen. &lt;br /&gt;
&lt;br /&gt;
Das vielleicht bekannteste Beispiel ist die Umsetzung von 0V/5V [[TTL]] Logikpegeln auf die -12V/12V Pegel einer seriellen [[RS232]] Schnittstelle. &lt;br /&gt;
&lt;br /&gt;
Die Probleme beim Pegelwandeln können sein:&lt;br /&gt;
&lt;br /&gt;
# Überlastung einer oder beider Seiten&lt;br /&gt;
# Inkompatible Logikpegel und daraus resultierendes Nichtfunktionieren der Schaltung, oder noch schlimmer sporadische Fehlfunktionen&lt;br /&gt;
# Verzögerungen der Signale durch die Pegelwandlung und daraus resultierende maximale Signalfrequenzen&lt;br /&gt;
&lt;br /&gt;
=== Überlastung ===&lt;br /&gt;
&lt;br /&gt;
Das Erzeugen von verschiedenen Versorgungsspannungen ist ziemlich einfach, aber man muss sicher gehen, daß man die Signalleitungen zwischen den Bauteilen überprüft. Wenn ein 5V Bauteil ein Signal an ein 3V Bauteil schickt können beide Bauteile beschädigt werden. Vor allem für neue ICs ist es ein Problem mit &amp;quot;hohen&amp;quot; Spannungen wie 5V zu arbeiten. Auf grund der immer kleineren Schaltkreistrukturen (der aktuelle Pentium wird mit 45nm Technologie hergestellt!) werden auch die Abstände und Schichtdicken immer geringer. Das reduziert natürlich auch die Spannungsfestigkeit der Transistoren auf dem IC. Neue ICs vertragen deshalb meist nur noch 3.3V, teilweise sogar weniger! Die Überlastung erfolgt durch zu hohe Spannung und dadurch mehr oder weniger langsame Zerstörung des ICs.&lt;br /&gt;
&lt;br /&gt;
=== Schutzdioden ===&lt;br /&gt;
&lt;br /&gt;
Hauptursache Nummer zwei für Überlastung von ICs mit verschiedenen Betriebsspannungen sind die in nahezu allen ICs integrierten Schutzdioden. Deren Aufgabe ist es in Normalfall, elektrostatische Entladungen auf eine sichere, niedrige Spannung zu begrenzen. Die Entladungen geschehen durch unsachgemässe Handhabung und Transport von ICs, z.B. wenn jemand über einen Kunstfaserteppich läuft, sich dabei elektrostatisch auflädt und einen IC anfasst. Oder wenn Bauteile in einem Gerät eingebaut sind und der Anwender berührt offen liegende Kontakte (RS232 Eingang, USB-Stick, PCI-Steckkarten beim Einbau etc.). Die Schutzdioden beginnen Strom zu leiten, wenn die Eingangsspannung ca. 500mV über VCC ansteigt oder mehr als 500mV unter GND absinkt. Im Normalbetrieb sollten die Schutzdioden keinen Strom leiten. Manchmal kann man sie aber zur Spannungsbegrenzung missbrauchen, siehe [[#STEP-DOWN:_5V_-.3E_3.3V | Step Down mit Vorwiderstand]].&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_schutzdioden.png]]&lt;br /&gt;
&lt;br /&gt;
=== 5V tolerante Eingänge ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;5 Volt tolerant&amp;lt;/i&amp;gt; bedeutet, daß 3 Volt Bausteine ohne Probleme von einem 5 Volt Baustein angesteuert werden können.&lt;br /&gt;
&lt;br /&gt;
Viele Bauteile die auf 3V arbeiten verfügen über 5V tolerante Eingänge. Man sollte aber grundsätzlich im Datenblatt darüber nachlesen, bevor die Schaltung aufgebaut wird. Sind sie es nicht, so ist ein &amp;lt;b&amp;gt;Pegelwandler&amp;lt;/b&amp;gt; auf den Verbindungsleitungen zwischen den Bauteilen notwendig. Ein Pegelwandler kann eine einfache Zener-Diode mit einem Widerstand sein, es kann aber auch ein eigens dafür vorgesehener IC sein. Sind die Signalwege bidirektional, so wird man meist die Lösung mit einem eigenen IC bevorzugen.&lt;br /&gt;
&lt;br /&gt;
[[AVR]]s sind generell nicht 5V tolerant, wenn sie mit 3.3V betrieben werden. Die absolute obere Grenze für Eingangsspannungen liegt bei Vcc + 0.5V. Zu finden in den elektrischen Spezifikationen im Datenblatt.&lt;br /&gt;
&lt;br /&gt;
Ob ein Bauteil 5V-tolerant ist und unter welchen Betriebsbedingungen das gilt steht im Datenblatt des betreffenden Bauteils vom betreffenden Hersteller. Wenn es auf diese Eigenschaft ankommt, lieber genau bei Lieferanten nachsehen, von welchem Hersteller die Bauteile kommen.&lt;br /&gt;
&lt;br /&gt;
Vorsicht bei:&lt;br /&gt;
&lt;br /&gt;
* 74&#039;&#039;&#039;LVX&#039;&#039;&#039;xxxx und 74&#039;&#039;&#039;LCX&#039;&#039;&#039;xxxx (245, 244, 240 ...) an Vcc=3,3V.&amp;lt;br&amp;gt;&amp;lt;font color=FF0000&amp;gt;Achtung&amp;lt;/font&amp;gt;: Nicht alle 74LVX sind für 5V -&amp;gt; 3,3V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
&lt;br /&gt;
=== Kompatibilität von Logikpegeln ===&lt;br /&gt;
&lt;br /&gt;
Siehe auch http://www.interfacebus.com/Design_Translation.html&lt;br /&gt;
&lt;br /&gt;
Verschiedene Mikroprozessoren haben eigene elektrische Kenndaten für HIGH und LOW Pegel, die abhängig von der Versorgungsspannung sind z.B. der [[R8C]]:&lt;br /&gt;
&lt;br /&gt;
* HIGH grösser 0.8 * Vcc&lt;br /&gt;
* LOW kleiner 0.2 * Vcc&lt;br /&gt;
&lt;br /&gt;
Man muss die Spannungen der Aus-und Eingänge vergleichen. Wenn es um ein Hobbyprojekt geht kann man einfach messen. Wenn es um eine kommerzielle Anwendung geht die man verkaufen will, sollte man besser die Spezifikationen der ICs studieren.&lt;br /&gt;
&lt;br /&gt;
== UNIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
=== STEP-UP: 3.3V -&amp;gt; 5V ===&lt;br /&gt;
&lt;br /&gt;
* 3.3V Pegel werden bei TTL kompatiblen Eingängen richtig erkannt (Schaltschwelle 1,4V). Es ist kein Pegelwandler erforderlich. Direkte Verbindung.&lt;br /&gt;
&lt;br /&gt;
* 5V CMOS Eingänge haben typisch eine minimale Eingangsspannug für HIGH (&amp;lt;math&amp;gt;V_{IH}&amp;lt;/math&amp;gt;) von 0.6 * VCC = 0.6 * 5V = 3V. Das kann ein 3.3V CMOS Ausgang direkt treiben, allerdings kann sich das Zeitverhalten dadurch etwas ändern weil der HIGH Pegel später erkannt wird. Vorsicht! Viele 5V CMOS ICs wollen für HIGH offiziell mindestens 0.7V * VCC = 3.5V oder manche auch 0.8 * VCC = 4.0V! Das geht dann offiziell nicht mehr mit einem 3.3V Ausgang! Für Hobbyzwecke kann man das aber ggf. probieren.&lt;br /&gt;
&lt;br /&gt;
* 3.3V [[Ausgangsstufen_Logik-ICs | Open Collector]] nach 5V (TTL oder CMOS): Einfach einen Pull-Up Widerstand hinzufügen und gut. Allerdings verbraucht der Pull-Up Widerstand bei LOW relativ viel Strom und kann bei HIGH nicht allzuviel Strom liefern. Die Schaltgeschwindigkeit von LOW nach HIGH wird durch die Grösse des Pull-Ups bestimmt.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
* 3,3V auf echte 5V (CMOS) geht am einfachsten mit einem Baustein der HCT Familie (NICHT HC !). Diese haben TTL-compatible Eingänge und echte CMOS Ausgänge&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator in nichtinvertierender Schaltung benutzen (LM339/393). Allerdings ist diese Lösung meist relativ langsam, abhängig vom verwendeten Komparator.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_comp_3-5.png]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* 74HCTxxx (245, 244, 240 ...)&lt;br /&gt;
* 74HCT125&lt;br /&gt;
* SN74LVC07AD&lt;br /&gt;
&lt;br /&gt;
=== STEP-UP: 5V -&amp;gt; 9..15V ===&lt;br /&gt;
&lt;br /&gt;
* Am einfachsten geht das mit einem Open Collector Ausgang, einfach einen Pull-Up hinzufügen (an die hohe Spannung) und fertig.&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-12.png]]&lt;br /&gt;
&lt;br /&gt;
* Man kann einen Komparator benutzen. Allerdings ist diese Lösung meist relativ langsam, abhängig vom verwendeten Komparator. Wenn nur zwei Signale pegelgewandlet werden müssen bietet sich der LM393 an, ein Doppelkomparator mit Open Collector Ausgang, mit dem man auf einen beliebigen Pegel ausgeben kann. Der LM339 ist ein Vierfachkomparator mit den gleichen Eigenschaften. Wenn wenig Platz vorhanden ist, dann ist der TL311 im winzigen SOT-23 Gehäuse sehr empfehlenswert. Bei jedem Komparator kann auch einfach eine Invertierung gemacht werden, einfach die Eingänge + und - vertauschen. Diese Komparatoren eignen sich bis ca. 1 MHz.&lt;br /&gt;
&lt;br /&gt;
* [http://www.elektronik-kompendium.de/sites/praxis/bausatz_pegelwandler-mit-transistoren.htm Pegelwandler mit Transistor, invertierend]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_inv.png]]&lt;br /&gt;
&lt;br /&gt;
* Pegelwandler mit Transistor, nicht invertierend&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_trans_ni.png]]&lt;br /&gt;
&lt;br /&gt;
Die Idee ist einfach. Wenn der Ausgang des 5V Gatters auf HIGH ist dann ist der Transistor ausgeschaltet, der Pull-Up Widerstand R7 zeiht den Ausgang auf +12V. Ist der Ausgang des 5V Gatters auf LOW ist, dann ist er vollkommen durchgesteuert und der Ausgang nahe 0V (je nach Typ ca. 300mV). Der Vorteil ist hier erhöhte Störsicherheit im Gegensatz zur einfachen Ansteurung der Basis über einen Vorwiderstand. Ausserdem wird dadurch nicht die Logik invertiert. Nachteilig ist der geringe Strom, der bei HIGH zur Verfügung steht (typisch 100&amp;amp;mu;A).&lt;br /&gt;
&lt;br /&gt;
* Wenn mehr Geschwindigkeit, Ausgangsstrom und weniger Stromverbrauch nötig ist, dann muss ein spezieller Baustein her, wie z.B.&amp;lt;BR&amp;gt;&amp;lt;BR&amp;gt;&lt;br /&gt;
** MOSFET-Treiber (z.B. L293, L298, UCC27325 und deren Verwandte), wenns nicht zu schnell ist (einige Dutzend kHz)&lt;br /&gt;
** CD40109, bei Reichelt verfügbar&lt;br /&gt;
** HEF4104, 4fach LOW-HIGH Pegelwandler mit normalen und invertierten Augängen sowie Tristate. Um GGf sicherzustellen, dass wie im Datenblatt beschrieben immer VDDI &amp;lt;= VDDO ist, kann man einfach eine Diode von VDDO nach VDDI schalten (z.B. Schottky SB120, aber auch 4148 &amp;amp; Co sollte problemlos funktionieren)&lt;br /&gt;
** CMOS 4504, 6fach LOW-HIGH/HIGH-LOW 3-20V Pegelwandler, TTL/CMOS (umschaltbar) =&amp;gt; CMOS, kein Reihenfolge von Vcc/Vee erforderlich (Bezugsquelle: CSD)&lt;br /&gt;
** MAX232, der braucht nur 5V Versorgungsspannung. Allerdings ist der Ausgangswiderstand relativ hoch (ca. 300Ohm) und man kann nur ca. 5mA Ausgangstrom liefern. Die Ausgangsspannung beträgt maximal 10V.&lt;br /&gt;
&lt;br /&gt;
=== STEP-DOWN: 5V -&amp;gt; 3.3V ===&lt;br /&gt;
&lt;br /&gt;
* Zuerst sollte man prüfen, ob die Eingänge 5V tolerant sind, Dann kann man die ICs direkt verbinden. Sehr schnell und billig!&lt;br /&gt;
&lt;br /&gt;
* Wenn die Eingänge nicht 5V tolerant sind und es trotzdem schnell sein soll, muss ein Gatter aus der LVC oder AHC Familie dazwischen geschaltet werden. Bei 3V Betriebsspannung kann man problemlos 5V an den Eingang anlegen. Der Baustein 74HC4050 erlaubt per Definition eine Step-Down Pegelwandlung bis etwa 15V (siehe Datenblatt). Beide Anordungen verbrauchen auch sehr wenig Ruhestrom.&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:* 74LVC245A (&#039;A&#039; ist wichtig, I/Os 5V-tolerant)&lt;br /&gt;
:* 74LVC245DW &lt;br /&gt;
:* 74LVT245 &lt;br /&gt;
:* 74LVXxxx (245, 244, 240 ...) an Vcc=3,3V. Achtung: Nicht alle 74LVX sind für 5V -&amp;gt; 3,3V geeignet, da jeder Hersteller die ICs anders baut!&lt;br /&gt;
:** 74LVX04 &lt;br /&gt;
:** 74LVX244 (Fairchild)&lt;br /&gt;
:** 74LVX245 (nicht von Reichelt, nicht 5V tolerant)&lt;br /&gt;
&lt;br /&gt;
:* 74HC4050 (bis 15V Step Down Pegelwandlung laut Datenblatt, bei Reichelt in DIP und SO erhältlich)&lt;br /&gt;
:* MAX3373/MAX3375&lt;br /&gt;
:* NC7SZ08 oder andere aus derselben Serie. CMOS-Logik mit 5V-toleranten Eingängen, recht flott und braucht dank SOT-23 auch wenig Platz auf dem Print&lt;br /&gt;
&lt;br /&gt;
* 5V Open Collector auf 3.3V Eingang. Einfach einen Pull-Up hinzufügen (Pull-Up liegt auf 3.3V). Nachteilig ist der relativ hohe Stromverbrauch bei LOW, die begrenzte Geschwindigkeit bei hochohmigen Pull-Ups und der relativ geringe Ausgangsstrom bei HIGH (abhängig vom Pull-Up).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_oc_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* Spannungsteiler mit 680 Ohm und 1kOhm. Der Nachteil dieser Lösung ist der relativ hohe Stromverbrauch (~3mA), der relativ geringe Ausgangsstrom (mehr als 200..300uA sollte man da nicht rausziehen) un die relativ geringe Geschwindigkeit (ca. 10 MHz).&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_st_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
* 1 kOhm Vorwiderstand. Dadurch wird der Strom vom 5V Ausgang in die 3.3V Versorgung durch die internenen Schutzdioden auf ca. 1mA begrenzt. Diese Lösung ist auch relativ langsam (ca. 5MHz). Ggf. kann man den Vorwiderstand auf 100 Ohm reduzieren, das erhöht dann wieder die Geschwindigkeit. Aufpassen, einige ICs vertragen nur 1mA oder weniger durch die Schutzdioden! Ausserdem muss man aufpassen, das jetzt von der 5V Seite Strom in die 3.3V Verorgung eingespeist wird. Besonders in Schaltungen mit sehr niedrigem Stromverbrauch kann das zum Problem werden, wenn weniger Strom verbraucht wird als über die Vorwiderstände eingespeist wird. Dann nimmt es meist der Spannungsregler für 3.3V übel wenn jemand &amp;quot;schiebt&amp;quot;, sprich, Strom einspeist. Denn die allermeisten Spannungsregler können nur Strom liefern (source), aber keinen Strom aufnehmen (sink). Es gibt 4-fach-Diodennetzwerke, die die internen Schutzdioden entlasten können, außerdem ist teilweise noch eine Zenerdiode enthalten, die diesen sink-Strom aufnehmen. &lt;br /&gt;
&lt;br /&gt;
[[bild:pw_vw_5-3.png]]&lt;br /&gt;
&lt;br /&gt;
Achtung: Mindestens für 74HC(T) Gatter ist dokumentiert (Philips 74HC/T High-Speed CMOS User Guide), dass auch schon geringer Strom durch die internen Schutzdioden zu einer unerwünschten Kopplung von Eingängen führen kann, d.h. der Strom fliesst zu einem anderen Eingang wieder hinaus. Sind also andere Eingänge ebenso hochohmig angeschlossen, kann dieser Querstrom zu Fehlfunktion führen.&lt;br /&gt;
&lt;br /&gt;
== BIDIREKTIONAL ==&lt;br /&gt;
&lt;br /&gt;
=== 5V &amp;lt;-&amp;gt; 3.3V ===&lt;br /&gt;
&lt;br /&gt;
* Wenn die 5V Seite TTL-kompatible Eingänge hat kann wieder der Spannungsteiler oder Vorwiderstand wie bei der unidirektionalen Anpassung verwendet werden (mit all seinen Vor- und Nachteilen).&lt;br /&gt;
&lt;br /&gt;
* Für bidirektionale Busse gibt es spezielle Pegelwandler mit 2 Versorgungsspannungen. Allerdings brauchen die meist ein Signal zur Richtungsumschaltung. Auch muss man die Reihenfolge der Versorgungsspannungen beim Einschalten beachten. Aktive bidirektionale Pegelwandler OHNE Steuereingang zur Richtungsumschaltung sind mit Vorsicht zu geniessen, denn die brauchen teilweise kurzzeitig einen relativ hohen Strom, um die Eingänge zu treiben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bauteile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* SN74CB3T3306 &lt;br /&gt;
* MAX1741 &lt;br /&gt;
* MAX3378E &lt;br /&gt;
* 74AHC126 s.u.&lt;br /&gt;
* ST2378 (bei CSD erhältlich, 3.5 eur, leider TSSOP)&lt;br /&gt;
&lt;br /&gt;
== Mit galvanischer Trennung ==&lt;br /&gt;
&lt;br /&gt;
* [[Optokoppler]]&lt;br /&gt;
&lt;br /&gt;
[[bild:pw_opto.png]]&lt;br /&gt;
&lt;br /&gt;
* GMR-Koppler von der Firma NVE &lt;br /&gt;
* iCoupler Technologie von der Firma Analog Devices&lt;br /&gt;
&lt;br /&gt;
Lit.: &#039;&#039;Galvanische Trennung: Optokoppler, GMR-Koppler oder iCoupler?&#039;&#039;, Siegfried W. Best, Redaktion elektronik industrie, [http://www.elektronik-industrie.de/ei/11,2003/article/2f0082f824c.html elektronik industrie 11-2003, S. 22ff.] &lt;br /&gt;
&lt;br /&gt;
== Praktische Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Einfaches RS232-Interface ===&lt;br /&gt;
&lt;br /&gt;
[http://web.archive.org/web/20050122013618/http://www.henrik-reimers.de/control/rs232interface.gif Erfolgreicher Einsatz bis 19200 Baud und bis zu 10 m Leitungslänge]&lt;br /&gt;
&lt;br /&gt;
Beschränkungen:&lt;br /&gt;
&lt;br /&gt;
* ggf. Platzbedarf&lt;br /&gt;
* Geschwindigkeit s.o.&lt;br /&gt;
&lt;br /&gt;
Beispiel: http://www.hagtech.com/pdf/translator.pdf&lt;br /&gt;
&lt;br /&gt;
=== [[I2C]]-Bus: gemeinsam 3.3V und 5V ===&lt;br /&gt;
&lt;br /&gt;
* [[MSP430]] an 3,3V/5V: http://www-s.ti.com/sc/psheets/slaa148/slaa148.pdf&lt;br /&gt;
&lt;br /&gt;
* Philips PCA9515: I2C Puffer mit Pegelwandlung. Der PCA9515 ist ein I2C-Bus Repeater, welcher I2C Busse mit verschiedenen Spannungen isoliert. Verfügbar bei DigiKey.&lt;br /&gt;
&lt;br /&gt;
* [http://www.standardics.philips.com/support/documents/i2c/pdf/an97055.pdf Philips AN97055 Bi-directional level shifter for I²C-bus and other systems]&lt;br /&gt;
&lt;br /&gt;
* Bevor man ein Philips I2C Chip auswählt sollte man prüfen ob er verfügbar ist und auch das verfügbare Gehäuse wählen. Man sollte auch überlegen ob ein Puffer wirklich gebraucht wird. Wenn man echte I2C ICs mit 5V betreibt, dann sind die Eingänge vom Typ Schmitt Trigger CMOS (z.B. PCF8574). Dann müssen 3.3V Pegel auf 5V umgesetzt werden. Wenn man jedoch SMBUS Ics verwendet (z.B. ADT7461, Silabs 8051) dann sind die Schwellspannungen TTL kompatibel und es ist keine Anpassung notwendig. Für neue Pegelwandler sollte man hier nachschauen. http://www.bus-buffer.com&lt;br /&gt;
&lt;br /&gt;
* [http://www.edn.com/article/CA193193.html &amp;quot;Two-transistor circuit replaces IC&amp;quot;]. Für diese Anwendung kann ENABLE direkt mit 3.3V verbunden werden. Es ist eigentlich nur dazu da, den ICs &amp;quot;hot-swappable&amp;quot; zu machen (kann unter Spannung gesteckt und getrennt werden). Man sollte beachten, daß die Schaltung sowohl für SCL als auch SDA benötigt wird.&lt;br /&gt;
&lt;br /&gt;
* Noch einfachere Lösungen mit nur einem MOSFET und zwei Pull-Up Widerständen pro Leitung sind in den folgenden Links zu finden. Vielleicht ist es sogar noch billiger Bipolartransistoren zu verwenden.&lt;br /&gt;
* http://www.semiconductors.philips.com/markets/mms/protocols/i2c/facts/#levelshifting&lt;br /&gt;
* http://www.semiconductors.philips.com/acrobat_download/literature/9398/39340011.pdf&lt;br /&gt;
&lt;br /&gt;
=== Auswählbare Pegel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ein CMOS Logikpegel zwischen 1,8V, 2,5V und 3,3V (abhängig von der Anwendung) muss auf 5V CMOS Logikpegel gewandelt werden. Es geht nur um diese Richtung mit maximal 8MHz. Es gibt die Stromversorgung für alle Pegel. Ein normaler Komparator wie LM311 ist nicht möglich, da er beim Betrieb mit 5V Versorgunsspannung erst ab 1V zu schalten anfängt. Meine Idee ist die Verwendung eines High Speed OPVs mit R2R Eingang, z.B. LMH6645.&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* Man könnte einen ultra-low threshold N-Kanal MOSFET nehmen und als Open Drain mit einem Pull-Up nach 5V betreiben, BSH103 könnte passen (Schwellspannung ~0,4V).&lt;br /&gt;
* High-Speed Single Supply Komparator wie z.B. [http://www-s.ti.com/sc/ds/tl712.pdf TL712] .&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frage:&#039;&#039;&#039;&lt;br /&gt;
Ich suchen einen IC, welcher eine Pegelwandlung von 3,3V nach 1,8V, 2,0V oder 5V ermöglicht und während des Betriebs umgeschaltet werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Antworten:&#039;&#039;&#039;&lt;br /&gt;
* So ein IC ist der Linear [http://www.linear.com/pc/productDetail.jsp?navId=H0,C1,C1007,C1071,P1601 LTC1555L-1.8] . &lt;br /&gt;
&lt;br /&gt;
=== AVR &amp;lt;-&amp;gt; [[MMC]] ===&lt;br /&gt;
&lt;br /&gt;
5V AVR an eine MMC.&lt;br /&gt;
* [http://www.microsyl.com/ledsign/ledsign.html Projektseite] &lt;br /&gt;
* [http://www.microsyl.com/ledsign/ledsign1.pdf Schaltplan]&lt;br /&gt;
&lt;br /&gt;
Der MAX3378E ist günstig zur Anbindung von 5V Mikrocontrollern an 3.3V SD/MMC Karten. Verfügbar in kleinem Gehäuse, bidirektional ohne Richtungsumschaltung und wenig Pins, ideal für SPI.&lt;br /&gt;
* [http://www.maxim-ic.com/appnotes.cfm/appnote_number/1159 MAX3378E]&lt;br /&gt;
* &#039;&#039;&#039;74LVC245&#039;&#039;&#039; mit 3.3V Betriebsspannung geht auch gut&lt;br /&gt;
&lt;br /&gt;
=== µC &amp;lt;-&amp;gt; Parallelport ([[ISP]]-Dongle, [[JTAG]] Wiggler, ...) ===&lt;br /&gt;
&lt;br /&gt;
Dieser Schaltplan funktioniert auch bei 3.3V wenn man einen 74&amp;lt;B&amp;gt;HC&amp;lt;/B&amp;gt;244 anstatt eines 74&amp;lt;B&amp;gt;LS&amp;lt;/B&amp;gt;244 verwendet: [http://www.epanorama.net/circuits/parallel_output.html Parallel port interfacing made easy: Simple circuits and programs to show how to use PC parallel port output capabilities].&lt;br /&gt;
&lt;br /&gt;
= Bauteile =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;74ALVC16245&#039;&#039;&#039; - &#039;&#039;16bit dual supply translating transceiver&#039;&#039;. Eine Seite von 1.5V bis 3.6V, die andere von 1.5 bis 5.5V.&lt;br /&gt;
* &#039;&#039;&#039;74LVX573&#039;&#039;&#039; (unidirektional, Latch, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX245&#039;&#039;&#039; (bidirektional, nicht alle Hersteller bauen diesen 5V tolerant!)&lt;br /&gt;
* &#039;&#039;&#039;74LVX125&#039;&#039;&#039; - &#039;&#039;Low Voltage Quad Buffer with 3-STATE Outputs&#039;&#039;. http://www.fairchildsemi.com/pf/74/74LVX125.html&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC2T45&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;SN74LVC8T245&#039;&#039;&#039; - &#039;&#039;8-Bit Dual-Supply Bus Transceiver with Configurable Voltage Translation and Three-State Outputs&#039;&#039;. http://focus.ti.com/docs/prod/folders/print/sn74lvc8t245.html&lt;br /&gt;
* &#039;&#039;&#039;74LCX244MSA&#039;&#039;&#039; von Fairchild.&lt;br /&gt;
* &#039;&#039;&#039;MAX3377&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;MAX3000&#039;&#039;&#039; 8-Kanal bidirektioneler Pegelwandler ohne Richtungsumschaltung&lt;br /&gt;
&lt;br /&gt;
Vierfachdioden im kleinen 6-poligen SMD-Gehäuse:&lt;br /&gt;
* http://www.st.com/stonline/products/literature/ds/12635/dsilc6-4xx.pdf&lt;br /&gt;
* http://www.st.com/stonline/products/literature/ds/11599.pdf&lt;br /&gt;
* http://www.st.com/stonline/products/literature/ds/6477/dalc208.pdf&lt;br /&gt;
* http://www.diodes.com/datasheets/ds30195.pdf&lt;br /&gt;
* http://www.littlefuse.com/data/en/Data_Sheets/SP724Lead_Free.pdf&lt;br /&gt;
&lt;br /&gt;
= Weblinks =&lt;br /&gt;
&lt;br /&gt;
* Gaurang Kavaiya, [http://www.edn.com/contents/images/6335309.pdf Don’t pay for level translators in systems using multiple power-supply voltages], EDN, MAY 25, 2006, 81-86 (PDF)&lt;br /&gt;
* http://www.elektronik-kompendium.de/public/schaerer/scf3_lc.htm&lt;br /&gt;
* http://www.prog-link.com/dcf77/dcf77-17.html&lt;br /&gt;
* http://elektronik.kai-uwe-schmidt.de/index.php?page=mp3_blueschaltung&lt;br /&gt;
* http://www.mikrocontroller.net/attachment.php/256452/levelshifter.pdf&lt;br /&gt;
* http://www.semiconductors.philips.com/acrobat_download/literature/9398/39340011.pdf&lt;br /&gt;
* http://www.mikrocontroller.net/forum/read-1-234277.html#new&lt;br /&gt;
* http://www.semiconductors.philips.com/acrobat/literature/9398/39340011.pdf&lt;br /&gt;
* http://www.st.com/stonline/products/literature/ds/5186/74lcx16245.pdf&lt;br /&gt;
* http://www.standardics.nxp.com/products/lvc/buffers/&lt;br /&gt;
* http://www.standardics.nxp.com/products/lvc/transceivers/&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;/div&gt;</summary>
		<author><name>FalkB</name></author>
	</entry>
</feed>